Memory leak on classes which refer to each other
For the curious: It turns out my memory leak had nothing to do with what I put in the sample here. I thought I had the issue nailed down to some sample code, but my sample code had different issues. I did end up finding my real issue though, and that's here: Ruby Symbol#to_proc leaks references in 1.9.2-p180?
I have two ruby classes (Generator
and Member
, in this example) where Generator serves as a factory (in a loose definition of the term) of Member objects, and each Member holds a reference to the Generator that constructed it.
Code:
class G开发者_高级运维enerator 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
Using IRB, I'd expect that if I simply call I simply call Member.get(Generator.new)
, but not actually assign the result to anything, both the reference to the newly-constructed Generator
object and the newly-constructed Member
object should have zero references laying around. So the garbage collector should collect both objects. But it only collects the Members, leaving the Generator sitting around:
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
, as I understand it, returns a list of references to a given class still on ruby's heap.)
Why is there still a reference to the Generator object sitting around? I haven't saved it to a variable in any way, so there shouldn't be anything referencing it anymore. The Member object got collected, so its instance variable that references the Generator class shouldn't be preventing it from getting collected.
I'm not just curious, either. We have a Sinatra app that has a similar class structure, and the equivalent Generator class stores a huge cache of Member objects, several hundred megs per request, and it never gets collected. Ruby runs out of memory and the app server has to restart every dozen or so requests.
When you call
Member.get(Generator.new)
you set the class instance variable @generator
for the Member class:
@generator = generator
And by calling the initial line Member.get(Generator.new), there is nothing that would create a Member, you simply create an instance of Generator, which is then assigned to the class instance variable.
That leaves:
- One instance of Generator assigned to Member class instance variable @generator
- No instance of Member was ever created
- The class instance variable @generator of Member will not get collected because the class Member will not get collected
-> The results you receive are perfectly normal, nothing wrong with Ruby's Garbage Collection.
精彩评论