2012-06-14 9 views
4

왜 unused_variable_2 및 unused_variable_3가 가비지 수집 되나 unused_variable_1은 아닌가요?왜 사용되지 않는 문자열이 가비지 수집되지 않습니까?

# leaky_boat.rb 
require "memprof" 

class Boat 
    def initialize(string) 
    unused_variable1 = string[0...100] 
    puts unused_variable1.object_id 
    @string = string 
    puts @string.object_id 
    end 
end 

class Rocket 
    def initialize(string) 
    unused_variable_2 = string.dup 
    puts unused_variable_2.object_id 
    unused_variable_3 = String.new(string) 
    puts unused_variable_3.object_id 
    @string = string 
    puts @string.object_id 
    end 
end 

Memprof.start 

text = "a" * 100 
object_id_message = "Object ids of unused_variable_1, @string, unused_variable_2, unused_variable_3, and another @string" 
before_gc_message = "Before GC" 
after_gc_message = "After GC" 
puts object_id_message 
boat = Boat.new(text) 
rocket = Rocket.new(text) 
puts before_gc_message 
Memprof.stats 
ObjectSpace.garbage_collect 
puts after_gc_message 
Memprof.stats 
Memprof.stop 

프로그램을 실행 :

$ uname -a 
Linux [redacted] 3.2.0-25-generiC#40-Ubuntu SMP Wed May 23 20:30:51 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux 
$ ruby --version # Have to use Ruby 1.8 - memprof doesn't work on 1.9 
ruby 1.8.7 (2011-06-30 patchlevel 352) [x86_64-linux] 
$ ruby -rubygems leaky_boat.rb 
Object ids of unused_variable_1, @string, unused_variable_2, unused_variable_3, and another @string 
70178323299180 
70178323299320 
70178323299100 
70178323299060 
70178323299320 
Before GC 
     2 leaky_boat.rb:6:String 
     2 leaky_boat.rb:26:String 
     1 leaky_boat.rb:9:String 
     1 leaky_boat.rb:7:String 
     1 leaky_boat.rb:32:Rocket 
     1 leaky_boat.rb:31:Boat 
     1 leaky_boat.rb:29:String 
     1 leaky_boat.rb:28:String 
     1 leaky_boat.rb:27:String 
     1 leaky_boat.rb:20:String 
     1 leaky_boat.rb:18:String 
     1 leaky_boat.rb:17:String 
     1 leaky_boat.rb:16:String 
     1 leaky_boat.rb:15:String 
After GC 
     1 leaky_boat.rb:6:String 
     1 leaky_boat.rb:32:Rocket 
     1 leaky_boat.rb:31:Boat 
     1 leaky_boat.rb:29:String 
     1 leaky_boat.rb:28:String 
     1 leaky_boat.rb:27:String 
     1 leaky_boat.rb:26:String 
+0

Andrew : 지난 주 내 대답이 만족스럽게 문제를 해결하지 못했습니까? – dbenhur

+0

@dbenhur 왜'unused_variable_2'와'unused_variable_3' *가 가비지 수집을하는지 설명하지 못합니다 - 메모리 할당을 저장하는 특별한 경우가 없습니까? –

+0

특수 공유 할당이 없습니다. String # dup과 String.new는 새로운 새로운 객체를 얻을 수 있음을 보장합니다. 내 대답은 코드 경로에 refs를 추가 할 것이다. – dbenhur

답변

6

SUBSTR에 대한 루비 버전의 캐릭터 구현이의 꼬리 인 SUBSTR을 할 때 메모리 할당을 저장하는 특별한 경우를 가지고 있기 때문에이 문제입니다 소스 문자열과 문자열 길이는 기본 객체 구조에 문자열 값을 저장하지 않을만큼 충분히 큽니다.

코드를 추적하면 string[0...100]의 범위는 this clause in rb_str_substr입니다. 따라서 새 문자열은 str_new3을 통해 할당되어 새로운 개체 구조체 (따라서 다른 object_id)를 할당하지만 문자열 값 ptr 필드를 원본 개체의 확장 저장소에 대한 포인터로 설정하고 ELTS_SHARED 플래그를 설정하여 새 개체 공유를 나타냅니다 다른 개체로 저장.

코드에서이 새 하위 문자열 객체를 가져 와서 가비지 수집을 실행할 때 여전히 라이브 참조 인 인스턴스 var @string에 할당합니다. 원래 문자열의 할당 된 저장소에 대한 실제 참조가 있으므로 수집 할 수 없습니다.

루비 트렁크에서 호환 가능한 테일 하위 문자열에 스토리지를 공유하기위한 최적화가 여전히 존재하는 것으로 보입니다.

두 개의 다른 vars unused_variable_2unused_variable_3은 별개의 저장소를 보장하는 메커니즘을 통해 설정되어 있기 때문에 확장 저장소 공유 문제가 없으므로 참조가 범위를 벗어 났을 때 예상대로 가비지 수집됩니다.

문자열 # dup은 소스 문자열의 내용을 원본 문자열의 내용으로 대체하고 저장소가 공유되지 않도록합니다. rb_str_replace (initialize_copy binding 통해)을 실행합니다.

문자열 #new (source_str)는 rb_str_init까지 실행되며, 이는 마찬가지로 제공된 초기 값에서 rb_str_replace와 별개의 저장을 보장합니다.

+0

@ NiklasB. 나는 그것을 읽지 않았습니다. 'beg + len == RSTRING (str) -> len'은 서브 문자열 조각의 beg + len이 소스 문자열의 len과 일치한다는 것을 말합니다. 즉, 문자열의 끝에 정렬됩니다. 그 이유는 코드 끝에 문자열의 끝 부분에 c-null char이 있어야하므로이 공유 저장소를 사용할 수있는 부분 문자열 조각은 문자열의 끝이 같은 부분 문자열뿐입니다. – dbenhur

+0

오, 미안하지만, 내 실수 야. 나는'string [0 ... 100]'의'0' 부분에 잘못 이끌 렸습니다. 이 경우 접미사 *와 접미사가 모두 있지만 접미사를 기준으로 실제로 공유됩니다. –

+0

"String # dup은 원본 문자열의 내용을 원본 문자열의 내용으로 대체하고 저장소가 공유되지 않도록 보장하는 rb_str_replace (initialize_copy 바인딩을 통해)를 실행합니다. - 즉, 하위 문자열 메서드에서 사용되는 최적화를 의도적으로 피할 수 있습니까? –

관련 문제