Ruby принудительно выполняет сборку мусора, не работая должным образом

В следующем коде:

class ExampleClass
  def initialize
    ObjectSpace.define_finalizer(self, proc{puts 'dead'})
  end
end

ex = ExampleClass.new
ex = nil

GC.start

while true
  # the while loop is to prevent the program from terminate
  # if the program does terminate, the finalizer gets called in the end like expected
end

Финализатор здесь никогда не вызывается и не выводится. Я ожидал, что сборщик мусора соберет ex, поскольку он был разыменован. Почему GC.start не заставляет собирать ex и немедленно вызывает finalizer to be called?


person aoiee    schedule 18.07.2017    source источник
comment
Я вижу, что dead печатается, если я закрываю ваш код, используя control+c, а также если я заменяю while true end на exit. Я на Ruby 2.5.0-dev между прочим. На какой платформе вы находитесь?   -  person max pleaner    schedule 18.07.2017
comment
Я на затмении, используя Ruby 2.4.0   -  person aoiee    schedule 18.07.2017
comment
о, а работает ли финализатор без GC.start?   -  person max pleaner    schedule 18.07.2017
comment
Мне интересно, почему dead не печатается правильно, когда я вызываю GC.start. Я поместил туда цикл while, чтобы показать, что программа не завершается.   -  person aoiee    schedule 18.07.2017


Ответы (1)


Я считаю, что при создании нового Proc (proc просто вызывает Proc.new в Kernel)

Создает новый объект Proc, привязанный к текущему контексту.

Означает, что он сохраняет ссылку на объект self и, следовательно, никогда не может быть разыменован сборщиком мусора для его сбора. Вместо этого вы должны создать proc внутри метода класса, чтобы контекст стал классом, а не экземпляром, который вы пытаетесь уничтожить.

class ExampleClass
  def initialize
    ObjectSpace.define_finalizer(self, self.class.finalize)
  end

  def self.finalize
    proc { puts "dead" }
  end

end


ex = ExampleClass.new
# needed to do something with the object as well, not sure why,
# but it won't work without this line either
puts "object id = #{ex.object_id}"
ex = nil

GC.start
while true
end

и это выводит

object id = 70223915005060
dead
^Cexample.rb:20:in `<main>': Interrupt
person Simple Lime    schedule 18.07.2017
comment
Большое спасибо за объяснение! Я попробовал, и это даже сработало без лишних puts "object id = #{ex.object_id}". - person aoiee; 18.07.2017
comment
Ах, да, я не помню, чтобы он был нужен раньше, но он не работал для меня без него, так что что-то в моей рубиновой реализации, я думаю, но я имею в виду, что в реальных примерах вы, вероятно, все равно используете объект для чего-то , а не просто создавать экземпляры, чтобы мы могли запустить финализатор. - person Simple Lime; 18.07.2017