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

jef*_*150 10 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.
Run Code Online (Sandbox Code Playgroud)

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

我在现实生活中使用的对象总共将数百万个元素推送到哈希中,但是哈希被用作临时查找表,并在一些循环完成后被清零.然而,这个查找表中的内存显然永远不会被释放,这会使我的应用程序骇人听闻地放慢速度,因为GC在每个周期都有数百万个已解散的对象需要分析.我正在使用sparsehashgem 进行一种解决方法,但这似乎不是一个棘手的问题,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)
Run Code Online (Sandbox Code Playgroud)

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

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

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

Jos*_*gts 1

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

正如 @Sergio Tulentsev 在评论中指出的那样,Ruby 可能不会将内存释放回内核。

这个 Ruby/Unix 邮件列表对话详细描述了这一点:避免系统调用

另外,这篇博文将分叉描述为 Rails 中内存管理的解决方案:使用 fork() 和写时复制在 Ruby on Rails 中节省内存。尽管如此,我认为在 Ruby 2 发布之前,Ruby 不会支持写时复制。