Утечка памяти на классах, которые ссылаются друг на друга - PullRequest
2 голосов
/ 30 августа 2011

Для любопытных : Оказывается, моя утечка памяти не имела ничего общего с тем, что я здесь добавил в пример. Я думал, что проблема была решена до некоторого примера кода, но у моего примера кода были другие проблемы. Я действительно обнаружил, что моя реальная проблема, и вот она: Ruby Symbol # to_proc пропускает ссылки в 1.9.2-p180?

У меня есть два класса ruby ​​(Generator и Member, в этом примере), где Генератор служит фабрикой (в произвольном определении термина) объектов-членов, и каждый член содержит ссылку на Генератор, который построил его.

Код:

class Generator
  def new_member
    Member.new
  end
end

class Member
  attr_reader :generator

  def self.get(generator)
    @generator = generator
    puts "Provided generator: #{generator}"
    generator.new_member
  end
end

Используя IRB, я ожидал бы, что если я просто позвоню, я просто вызову Member.get(Generator.new), но на самом деле не присваиваю результат чему-либо, как ссылку на вновь созданный объект Generator, так и на вновь созданный Member объект должен иметь нулевые ссылки, лежащие вокруг. Так что сборщик мусора должен собрать оба объекта. Но он только собирает участников, оставляя Генератор без дела:

ruby-1.9.2-p180 :001 > Member.get(Generator.new)
Provided generator: #<Generator:0x007fcf398015c8>
 => #<Member:0x007fcf39801550>
ruby-1.9.2-p180 :006 > GC.start
 => nil 
ruby-1.9.2-p180 :007 > ObjectSpace.each_object(Member){|m| puts m}
 => 0 
ruby-1.9.2-p180 :008 > ObjectSpace.each_object(Generator){|g| puts g}
#<Generator:0x007fcf398015c8>
 => 1

(ObjectSpace.each_object, насколько я понимаю, возвращает список ссылок на данный класс, все еще находящийся в куче рубина.)

Почему до сих пор остается ссылка на объект Generator, сидящий вокруг? Я никоим образом не сохранил его в переменной, поэтому больше не должно быть ссылок на него. Объект Member был собран, поэтому его переменная экземпляра, которая ссылается на класс Generator, не должна препятствовать его сбору.

Мне тоже не просто любопытно. У нас есть приложение Sinatra, которое имеет схожую структуру классов, а эквивалентный класс Generator хранит огромный кэш объектов Member, несколько сотен мегабайт на запрос, и его никогда не собирают. В Ruby не хватает памяти, и серверу приложений приходится перезапускать каждые дюжину или около того запросов.

1 Ответ

7 голосов
/ 30 августа 2011

Когда вы вызываете

Member.get(Generator.new)

, вы устанавливаете переменную экземпляра класса @generator для класса Member:

@generator = generator

И вызывая начальную строкуMember.get (Generator.new), нет ничего, что могло бы создать Member, вы просто создаете экземпляр Generator, который затем назначается переменной экземпляра класса.

То есть:

  • Один экземпляр Generator назначен переменной экземпляра класса Member @ generator
  • Нет экземпляр Member когда-либо был создан
  • Переменная экземпляра класса @generator Member будетне собраны, потому что ученики не будут собраны

-> Полученные результаты совершенно нормальны, в Ruby's Garbage Collection нет ничего плохого.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...