相互引用的类上的内存泄漏

发布于 2024-12-02 04:07:12 字数 1607 浏览 1 评论 0原文

对于好奇的:事实证明,我的内存泄漏与我在此处放入示例的内容无关。我以为我的问题已确定为某些示例代码,但我的示例代码有不同的问题。不过,我最终确实找到了我的真正问题,那就是:Ruby Symbol#to_proc 在 1.9.2-p180 中泄漏引用?

我有两个 ruby​​ 类(GeneratorMember,在此示例中),其中 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

神爱温柔 2024-12-09 04:07:12

当您调用时,

Member.get(Generator.new)

您为 Member 类设置类实例变量 @generator

@generator = generator

并且通过调用初始行 Member.get(Generator.new),没有什么会创建一个Member,您只需创建一个Generator 实例,然后将其分配给类实例变量。

剩下的就是: 将

  • Generator 的一个实例分配给 Member 类实例变量 @generator
  • 从未创建过 Member 的实例 Member 的
  • 类实例变量 @generator 将不会被收集,因为类 Member 不会被收集

- >您收到的结果完全正常,Ruby 的垃圾收集没有任何问题。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文