2012-06-14 3 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' * does * получают сбор мусора - не имеют ли они особых случаев для экономии памяти? –

+0

У них нет специальных общих распределений. String # dup и String.new гарантируют, что вы получите отличный новый объект. Я добавлю ссылки на код в свой ответ. – dbenhur

ответ

6

Такое поведение объясняется тем, что реализация струны версии рубина для подстроки имеет особый случай, чтобы сохранить выделение памяти, когда вы берете SUBSTR, который является хвостом исходная строка и длина строки достаточно велики, чтобы не сохранять строковое значение в структуре базового объекта.

Если вы проследите код, вы увидите, что индекс string[0...100] будет проходить через this clause in rb_str_substr. Таким образом, новая строка будет выделена через str_new3, которая выделяет новую структуру объекта (следовательно, отличается object_id), но устанавливает значение строки ptr в качестве указателя в расширенном хранилище исходного объекта и устанавливает флаг ELTS_SHARED, чтобы указать, что новый объект разделяет хранение с другим объектом.

В вашем коде вы берете этот новый объект подстроки и назначаете его экземпляру var @string, который по-прежнему является живой ссылкой, когда вы запускаете сборку мусора. Поскольку существует живая ссылка на выделенное хранилище исходной строки, ее невозможно собрать.

В рубиновом стволе эта оптимизация для совместного использования хранилища на совместимых хвостовых подстрочках по-прежнему существует.

У двух других варов unused_variable_2 и unused_variable_3 нет этой расширенной проблемы совместного использования хранилища, поскольку они устанавливаются с помощью механизмов, обеспечивающих различное хранилище, поэтому они собирают мусор, как ожидалось, когда их ссылки исчезают из области видимости.

String # dup запускает rb_str_replace (через initialize_copy binding), который заменяет содержимое исходной строки копией содержимого исходной строки и гарантирует, что хранилище не используется совместно.

String # new (source_str) пробегает rb_str_init, который также обеспечивает отличное хранилище с rb_str_replace при исходном начальном значении.

+0

@NiklasB. Не так, как я читал. 'beg + len == RSTRING (str) -> len' говорит, что beg + len подстрочного фрагмента соответствует len исходной строки, то есть они выравниваются в конце строки. Причина этого в том, что код должен иметь символ c-null в конце строки, поэтому единственными подстроками, которые могут использовать это разделяемое хранилище, являются те, которые имеют один и тот же конец строки. – dbenhur

+0

О, извините, моя ошибка. Я был введен в заблуждение частью '0'' string [0 ... 100] '. В этом случае это как префикс *, так и * суффикс, но обмен действительно работает на основе суффикса. –

+0

«String # dup запускает rb_str_replace (посредством привязки initialize_copy), который заменяет содержимое исходной строки копией содержимого исходной строки и гарантирует, что хранилище не используется совместно». - так, другими словами, он сознательно избегает оптимизации, используемой методом подстроки? –

Смежные вопросы