在Ruby MRI 1.9.3上的resque-scheduler进程中跟踪内存泄漏

时间:2014-10-18 19:08:43

标签: ruby memory-leaks garbage-collection ruby-1.9.3

我在Ruby MRI 1.9.3(作为Rails 3.2.13的一部分)环境中运行resque-scheduler,并且不知何故发生了内存泄漏,这让我感到疯狂。它不会发生在我的MacOS开发机器上(在正好相同的环境中运行),但发生在我们在AWS上运行的每台64位Ubuntu 12.04.4 LTS机器上。

在每次通过resque-scheduler主循环时,堆都在不断增长 - 尽管堆的已使用部分保持不变。这里是对GC.stat输出的一个示例,GC.stat在GC.start调用之后立即调用。

GC.stat: {:count=>151, :heap_used=>2424, :heap_length=>4361, :heap_increment=>1937, :heap_live_num=>840924, :heap_free_num=>145061, :heap_final_num=>0}
GC.stat: {:count=>155, :heap_used=>2428, :heap_length=>4368, :heap_increment=>1940, :heap_live_num=>840924, :heap_free_num=>146688, :heap_final_num=>0}
GC.stat: {:count=>159, :heap_used=>2432, :heap_length=>4375, :heap_increment=>1943, :heap_live_num=>840924, :heap_free_num=>148121, :heap_final_num=>0}
GC.stat: {:count=>164, :heap_used=>2437, :heap_length=>4384, :heap_increment=>1947, :heap_live_num=>840924, :heap_free_num=>150347, :heap_final_num=>0}
GC.stat: {:count=>170, :heap_used=>2443, :heap_length=>4395, :heap_increment=>1952, :heap_live_num=>840924, :heap_free_num=>152593, :heap_final_num=>0}
GC.stat: {:count=>176, :heap_used=>2449, :heap_length=>4406, :heap_increment=>1957, :heap_live_num=>840924, :heap_free_num=>155230, :heap_final_num=>0}
GC.stat: {:count=>181, :heap_used=>2454, :heap_length=>4415, :heap_increment=>1961, :heap_live_num=>840924, :heap_free_num=>157266, :heap_final_num=>0}
GC.stat: {:count=>191, :heap_used=>2464, :heap_length=>4433, :heap_increment=>1969, :heap_live_num=>840924, :heap_free_num=>161334, :heap_final_num=>0}

作为一个单独的问题,RUBY_FREE_MIN,RUBY_HEAP_MIN_SLOTS,RUBY_GC_MALLOC_LIMIT env vars似乎都没有对1.9.3 GC的行为产生任何影响。随着heap_increment的增长,问题只是GC认为由于某些硬编码设置而需要继续增加堆大小?

[编辑]

再看一遍,我可以看到heap_used正在上升。但是如果我考虑与GC.stat一起打印的ObjectSpace.count_objects,那就没有意义了:

GC.stat: {:count=>172, :heap_used=>2438, :heap_length=>4386, :heap_increment=>1948, :heap_live_num=>841064, :heap_free_num=>150208, :heap_final_num=>0}
ObjectSpace.count_objects: {:TOTAL=>991767, :FREE=>92284, :T_OBJECT=>45577, :T_CLASS=>10048, :T_MODULE=>2452, :T_FLOAT=>1038, :T_STRING=>449766, :T_REGEXP=>4537, :T_ARRAY=>213156, :T_HASH=>17273, :T_STRUCT=>591, :T_BIGNUM=>1605, :T_FILE=>6, :T_DATA=>91099, :T_MATCH=>17, :T_COMPLEX=>1, :T_RATIONAL=>436, :T_NODE=>57652, :T_ICLASS=>4229}
GC.stat: {:count=>173, :heap_used=>2439, :heap_length=>4388, :heap_increment=>1949, :heap_live_num=>840996, :heap_free_num=>150016, :heap_final_num=>0}
ObjectSpace.count_objects: {:TOTAL=>992173, :FREE=>150621, :T_OBJECT=>45073, :T_CLASS=>9922, :T_MODULE=>2452, :T_FLOAT=>636, :T_STRING=>403784, :T_REGEXP=>4502, :T_ARRAY=>207916, :T_HASH=>15856, :T_STRUCT=>591, :T_BIGNUM=>14, :T_FILE=>6, :T_DATA=>89977, :T_MATCH=>17, :T_COMPLEX=>1, :T_RATIONAL=>63, :T_NODE=>56639, :T_ICLASS=>4103}

当所有对象计数都下降时,heap_used如何上升?

1 个答案:

答案 0 :(得分:0)

在深入研究resque_scheduler代码后,我追溯了内存泄漏的罪魁祸首。它是(鼓)" Object.methods"。糟糕的resque_scheduler正试图在它即将入队的工作上找到一些钩子,并为此使用反射。我相信我们在resque_scheduler上只注意到这一点的原因是因为它是A运行而没有分叉且B.使用Object.methods的唯一守护进程。

以下代码在irb中运行时会泄漏大约8Mb的内存:

(1..1000).each do
  Object.methods
end

从红宝石的对象空间来看,一直没有使用额外的内存。

问题显然与我们正在使用的linux映像上的malloc有关。我在那里编译了最新的ruby 2.1.3,上面的例子泄漏了内存。然后我用jemalloc而不是stock malloc编译了最新的1.9.3(p547),上面的例子不再泄漏。所以,我相信这是解决方案。

如果有人能够更多地了解他们的想法,那就太棒了。