如何让Ruby发布未引用的内存?

时间:2012-09-19 03:01:38

标签: ruby memory

请考虑以下代码段:

l = []

while 1
  l << 'a random 369-characterish string'
end
^C
# ran this for maybe 4 seconds, and it had 27 million entries in l. memory
# usage was 1.6 GB.

l = nil

# no change in memory usage

GC.start

# memory usage drops a relatively small amount, from 1.6 GB to 1.39 GB.

我正在将数百万个元素推入/通过Ruby的数据结构,并且存在一些严重的内存问题。这个例子表明,即使在没有引用现存对象的情况下,即使在明确调用GC.start之后,Ruby也不会让它[大部分]进行。

我在现实生活中使用的对象总共将数百万个元素推送到哈希中,但是哈希被用作临时查找表,并在一些循环完成后被清零。然而,这个查找表中的内存显然永远不会被释放,这会使我的应用程序骇人听闻地放慢速度,因为GC在每个周期都有数百万个已解散的对象需要分析。我正在使用sparsehash gem进行解决方法,但这似乎不是一个棘手的问题,Ruby运行时应该像这样窒息。清楚地删除了引用,并且应该清楚地收集和处理对象。任何人都可以帮我弄清楚为什么没有发生这种情况?

我已经尝试l.delete_if { |x| true}关于freenode上#ruby中的用户的建议,但这真的很慢,而且似乎也没有引起明显的内存释放。

使用ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]

编辑:

为了进行比较,这是python3中的一次运行:

l = []

while 1:
    l.append('a random 369-characterish string')
^C
# 31,216,082 elements; 246M memory usage.

l = []
# memory usage drops to 8K (0% of system total)

在python2上测试显示几乎相同的结果。

我不确定这是否足以将这一点视为MRI中的实施缺陷,或者它是否只是用于不同的GC方法。无论哪种方式,似乎Python更适合使用通过数据结构总共推动数百万个元素的情况,并定期将结构归零(就像一个临时查找表一样)。

看起来这应该是一个简单的。 :\

1 个答案:

答案 0 :(得分:1)

有点hacky,但你可以尝试fork将操作作为一个单独的进程关闭。该进程将在共享内存空间中运行;当它终止时,内存将被释放。

Ruby可能没有像@Sergio Tulentsev在评论中指出的那样将内存释放回内核。

此Ruby / Unix邮件列表对话详细描述了这一点:Avoiding system calls

此外,这篇博客文章描述了分支作为Rails中内存管理的解决方案:Saving memory in Ruby on Rails with fork() and copy-on-write。虽然,我不认为Ruby会支持copy-on-write,直到Ruby 2出现。