好的,这是我第二次尝试调试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>
标签不会?)
答案 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
。