相互引用的类上的内存泄漏
对于好奇的:事实证明,我的内存泄漏与我在此处放入示例的内容无关。我以为我的问题已确定为某些示例代码,但我的示例代码有不同的问题。不过,我最终确实找到了我的真正问题,那就是:Ruby Symbol#to_proc 在 1.9.2-p180 中泄漏引用?
我有两个 ruby 类(Generator
和Member
,在此示例中),其中 Generator 充当 Member 对象的工厂(在该术语的松散定义中),并且每个 Member 都保存对构造它的 Generator 的引用。
代码:
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
,据我所知,返回对仍在 ruby 堆上的给定类的引用列表。)
为什么仍然存在对周围的 Generator 对象的引用?我没有以任何方式将它保存到变量中,所以不应该再有任何东西引用它。 Member 对象已被收集,因此引用 Generator 类的实例变量不应阻止其被收集。
我也不只是好奇。我们有一个 Sinatra 应用程序,它具有类似的类结构,并且等效的 Generator 类存储大量的 Member 对象缓存,每个请求数百兆,并且它永远不会被收集。 Ruby 内存不足,应用程序服务器必须每隔十几个请求重新启动一次。
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 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
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
当您调用时,
您为 Member 类设置类实例变量
@generator
:并且通过调用初始行 Member.get(Generator.new),没有什么会创建一个Member,您只需创建一个Generator 实例,然后将其分配给类实例变量。
剩下的就是: 将
- >您收到的结果完全正常,Ruby 的垃圾收集没有任何问题。
When you call
you set the class instance variable
@generator
for the Member class: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:
-> The results you receive are perfectly normal, nothing wrong with Ruby's Garbage Collection.