直升机,
在哈希中删除密钥后,我无法成功释放内存。当我从Hash中删除密钥时,内存不会被释放,也不会在手动调用GC.start
之后。当从Hash中删除密钥并且这些对象在某处泄漏时,这种预期行为或GC是否不释放内存?如何删除Ruby中的Hash键并在内存中取消分配?
示例:
irb(main):001:0> `ps -o rss= -p #{Process.pid}`.to_i
=> 4748
irb(main):002:0> a = {}
=> {}
irb(main):003:0> 1000000.times{|i| a[i] = "test #{i}"}
=> 1000000
irb(main):004:0> `ps -o rss= -p #{Process.pid}`.to_i
=> 140340
irb(main):005:0> 1000000.times{|i| a.delete(i)}
=> 1000000
irb(main):006:0> `ps -o rss= -p #{Process.pid}`.to_i
=> 140364
irb(main):007:0> GC.start
=> nil
irb(main):008:0> `ps -o rss= -p #{Process.pid}`.to_i
=> 127076
PS:我使用ruby 1.8.7。我也尝试过ruby 1.9.2,但它并没有更好。
答案 0 :(得分:6)
请参阅Stackoverflow: How Malloc and Free Work
出于各种好的理由(在上面的引文中详细说明),几乎没有内存管理器将内存释放回操作系统。
为了让您了解流程更改,Ruby解释器的C部分中的基础malloc
和free
将需要为主机操作系统提供内存。这不会发生,但在Ruby级别,对象已经过GC并且在解释器中位于本地保存的空闲列表中。
答案 1 :(得分:3)
作为一名高级开发人员,他在很多语言中长期使用这些东西,这是我的想法:
虽然我认为在使用C等编译语言时你的意图很好,但细粒度的开发人员控制的内存管理并不适合像Ruby,Python和Perl这样的语言。
Perl,Ruby和Python等脚本语言使我们免于内存管理的担忧。这是我们喜欢它们的原因之一。如果我们有可用的内存,他们将使用他们需要的东西来完成工作。内存管理控制的丢失是对开发速度和调试简易性的折衷;我们没有它,也不需要担心它。如果我需要它,我将使用C语言或汇编语言。
假设它是一个内存泄漏,那么,我认为这有点幼稚或冒昧。像你提到的内存泄漏将是一个重大的泄漏,并且,随着许多基于Ruby的应用程序和站点,很久以前有人会注意到它。所以,当我看到一些没有意义的东西时,作为一个理智的检查,我总是认为我在代码中做错了,然后我会看看我对某些东西是如何工作的假设,如果那些看似合理的,我会去寻找其他有类似问题的人,看看他们是否有解决方案。而且,如果问题是语言的核心问题,我会深入研究来源或与一些核心开发人员交谈,并询问我是否对我所看到的事情感到困惑。我之前发现了低级别的错误,但是他们已经成了极端情况,在我提到任何事情之前我花了几天时间挖掘,因为我不想像我的同行那样提交错误报告立即与Apple合作,然后发现这是他代码中的一个错误。
我在解除分配时将内存返回系统的总体思路是,它会产生额外的开销,这可能会在下一次浪费CPU周期的操作中被逆转,这种解释和脚本语言无法承受,因为它们不像编译语言开始。我认为,如果语言需要重复分配大块内存(如果必须这样做一次,特别是使用像Ruby这样的OO语言),这是一件公平的事情。在这一点上,坚持以前使用的内存是很有意义的。
并且,在大的计划中,考虑到我们在盒子中通常有多少空闲时间,分配1,000,000个这种大小的数组元素并不是很多内存。我更关心的是需要在内存中维护数组中的1,000,000个元素,并建议他们认真对待使用数据库。你可能有一个合理的商业理由将它全部保存在RAM中。如果是这样,最大化主机上的RAM,你应该没问题。
答案 2 :(得分:1)
对象应该是垃圾收集的。如果您要再次创建它们,则该过程不应该显着增长,因为它具有所有空白空间。但是,Ruby不会将该内存释放回操作系统,因为它假设将来可能会再次需要那么多内存。
这是一个相当简单的解释,但基本上,你所看到的是正常的。
答案 3 :(得分:0)
如果你运行两次分配,可以或多或少地看到@digitalross。如果确实存在这样的内存泄漏,您可能会希望内存大小翻倍,但这种情况不会发生。
[~]$ irb rvm:ruby-1.9.3-p0@global
1.9.3p0 :001 > `ps -o rss= -p #{Process.pid}`.to_i
=> 8148
1.9.3p0 :002 > a = {}
=> {}
1.9.3p0 :003 > 1000000.times{|i| a[i] = "test #{i}"}
=> 1000000
1.9.3p0 :004 > `ps -o rss= -p #{Process.pid}`.to_i
=> 101188
1.9.3p0 :005 > 1000000.times{|i| a.delete(i)}
=> 1000000
1.9.3p0 :006 > `ps -o rss= -p #{Process.pid}`.to_i
=> 90960
1.9.3p0 :007 > GC.start
=> nil
1.9.3p0 :008 > `ps -o rss= -p #{Process.pid}`.to_i
=> 93388
1.9.3p0 :009 > `ps -o rss= -p #{Process.pid}`.to_i
=> 93388
1.9.3p0 :010 > 1000000.times{|i| a[i] = "test #{i}"}
=> 1000000
1.9.3p0 :011 > `ps -o rss= -p #{Process.pid}`.to_i
=> 140088
1.9.3p0 :012 > 1000000.times{|i| a.delete(i)}
=> 1000000
1.9.3p0 :013 > `ps -o rss= -p #{Process.pid}`.to_i
=> 130880
1.9.3p0 :014 > GC.start
=> nil
1.9.3p0 :015 > `ps -o rss= -p #{Process.pid}`.to_i
=> 104256
在第一次运行结束时,进程报告内存大小为93388,在第二次运行后报告104256,内存使用量仅增加约10%。