Ruby Symbol#to_proc泄漏了1.9.2-p180中的引用?

时间:2011-08-31 20:53:12

标签: ruby memory-leaks

好的,这是我第二次尝试调试Sinatra应用程序的内存问题。我相信这次我把它固定在简单的示例代码中。

当我通过.map(&:some_method)过滤数组时,它会导致该数组中的项目无法收集垃圾。运行等效的.map{|x| x.some_method}完全没问题。

演示:给出一个简单的样本类:

class C
  def foo
    "foo"
  end
end

如果我在IRB中运行以下内容,则会正常收集:

ruby-1.9.2-p180 :001 > a = 10.times.map{C.new}
 => [...]
ruby-1.9.2-p180 :002 > b = a.map{|x| x.foo}
 => ["foo", "foo", "foo", "foo", "foo", "foo", "foo", "foo", "foo", "foo"]
ruby-1.9.2-p180 :003 > ObjectSpace.each_object(C){}
 => 10
ruby-1.9.2-p180 :004 > a = nil
 => nil
ruby-1.9.2-p180 :005 > b = nil
 => nil
ruby-1.9.2-p180 :006 > GC.start
 => nil
ruby-1.9.2-p180 :007 > ObjectSpace.each_object(C){}
 => 0

因此不再存在对C的引用。好。但是替换map{|x| x.foo} with map(&:foo)(被公布为等效的),它不会被收集:

ruby-1.9.2-p180 :001 > a = 10.times.map{C.new}
 => [...]
ruby-1.9.2-p180 :002 > b = a.map(&:foo)
 => ["foo", "foo", "foo", "foo", "foo", "foo", "foo", "foo", "foo", "foo"]
ruby-1.9.2-p180 :003 > ObjectSpace.each_object(C){}
 => 10
ruby-1.9.2-p180 :004 > a = nil
 => nil
ruby-1.9.2-p180 :005 > b = nil
 => nil
ruby-1.9.2-p180 :006 > GC.start
 => nil
ruby-1.9.2-p180 :007 > ObjectSpace.each_object(C){}
 => 10
ruby-1.9.2-p180 :008 >

这是一个红宝石虫吗?我会尝试更多版本的ruby来确定,但这似乎是一个明显的问题。谁知道我做错了什么?

编辑:

我在1.8.7-p352中尝试了这个,它没有问题。 1.9.3-preview1 确实但是仍有问题。是错误报告还是我做错了什么?

Edit2:格式化(为什么在每行之前放置四个空格会产生语法高亮,而<pre>标签不会?)

1 个答案:

答案 0 :(得分:3)

因为a.map(&:foo)应该与a.map{|x| x.foo}完全等价,所以看起来你真的遇到了Ruby代码中的错误。在(http://redmine.ruby-lang.org/)上提交错误报告并不会有什么坏处,可能发生的最糟糕的事情就是被忽略了。您可以通过提供问题的补丁来降低这种可能性。

编辑:我投入了我的IRB并尝试了您的代码。我可以重现您在ruby 1.9.2p290 (2011-07-09 revision 32553) [x86_64-linux]上描述的问题。但是,明确地在符号上调用to_proc不会遇到同样的问题:

irb(main):001:0> class C; def foo; end; end
=> nil
irb(main):002:0> a = 10.times.map { C.new }
=> [...]
irb(main):004:0> b = a.map(&:foo.to_proc)
=> [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil]
irb(main):005:0> ObjectSpace.each_object(C){}
=> 10
irb(main):006:0> a = b = nil
=> nil
irb(main):007:0> GC.start
=> nil
irb(main):008:0> ObjectSpace.each_object(C){}
=> 0

我们在这里看到了隐式Symbol -> Proc转换的问题。也许我稍后会尝试深入研究Ruby源代码。如果是这样,我会及时通知您。

编辑2:

问题的简单解决方法:

class Symbol
  def to_proc
    lambda { |x| x.send(self) }
  end
end

class C
  def foo; "foo"; end
end

a = 10.times.map { C.new }
b = a.map(&:foo)
p b
a = b = nil
GC.start
p ObjectSpace.each_object(C) {}

打印0