工作人员完成后 Sidekiq 未释放内存

Posted

技术标签:

【中文标题】工作人员完成后 Sidekiq 未释放内存【英文标题】:Sidekiq not deallocating memory after workers have finished 【发布时间】:2013-09-29 11:30:27 【问题描述】:

我有大约 六个 Sidekiq 工作人员,它们执行 JSON 抓取。根据端点的数据集大小,它们在 1 分钟到 4 小时之间完成。尤其是看长篇,需要 4 小时,我发现随着时间的推移,内存会略有增加。

这不是问题,直到我想再次安排相同的工作人员作业。内存不会被释放并堆积起来,直到我遇到了 Linux OOM Killer,它摆脱了我的 Sidekiq 进程。

内存泄漏?我观察了 ObjectSpace 中不同对象的数量:

ObjectSpace.each_object.inject(Hash.new(0))  |count, o| count[o.class] += 1 

那里并没有真正增加,散列、数组等的集合保持不变,短暂的增加被垃圾收集器扫除,gc.stat[:count] 告诉我,垃圾收集器也在工作。

即使在工人完成后,例如我得到了 [Done] 记录并且没有工人再忙了,内存没有被释放。原因是什么?我可以对此做些什么吗?写一个终结器?

当前唯一的解决方案:重启 Sidekiq 进程。

我正在使用 Ruby 2.0.0 并使用 Ruby MRI。


对于 JSON 解析,我使用 Yajl,因此是 C 绑定。我需要它,因为它似乎是唯一能够正确实现流式读写的快速 JSON 解析器。

【问题讨论】:

你用什么 gem 来解析输入的 JSON?您是否使用任何其他带有 C 扩展的 gem?从您的描述中听起来(内存使用量增加,但 Ruby 对象的数量是恒定的)就像您可能从具有 C 扩展名的 gem 中泄漏(例如,某些 gem 正在分配不用于存储 Ruby 对象的内存,并且永远不要释放它)。 也有可能你有一个纯 Ruby 的“泄漏”,你重复地改变一个对象并导致它的大小增长而不分配新的对象。例如,重复附加到 Ruby 字符串会导致它不断消耗更多内存,而不会增加对象数量。 @grumbler 哦,好点。我扩展了我的问题。我使用 Yajl 进行 JSON 解析,这确实是一个 C 绑定。从来没有想过这个。 如果我理解正确的话,Ruby 永远不会释放它曾经保留的内存,即使在垃圾回收期间也是如此。垃圾收集“释放”的内存实际上只是被标记为可以安全地被同一个 ruby​​ 进程重用。此外,如果您的 JSON 解析器使用符号化键将 json 转换为哈希,则不会对任何符号进行垃圾收集,符号将永远存在。 由于 Ruby 2.2 符号被垃圾收集:ruby-lang.org/en/news/2014/12/25/ruby-2-2-0-released 此外,Ruby 实际上将内存释放回操作系统:github.com/ruby/ruby/blob/… 【参考方案1】:

Mike Perham 在这里写了 Sidekiq:http://www.mikeperham.com/2009/05/25/memory-hungry-ruby-daemons/

tl;dr 版本: MRI 不会返还内存,您最多只能控制堆,为此,建议使用Ruby Enterprise Edition。

不知道这有什么帮助,但情况就是这样——直接从马的嘴里说出来。

【讨论】:

好指针@digitalextremist +1 旧线程,我知道,但想知道您是否知道这是否仍然适用于 Ruby 2.1 并且它是新的 GC? 我们仍然遇到同样的问题,即 MRI 无法恢复记忆。 同样,MRI 不会恢复记忆。这是一个关键问题,我希望我早点知道。 而且 Ruby 企业版已经停产,它基于 Ruby 1.8.7,甚至强烈不推荐。我目前正在尝试将我的应用程序转换为 JRuby。

以上是关于工作人员完成后 Sidekiq 未释放内存的主要内容,如果未能解决你的问题,请参考以下文章

程序结束后未释放的内存会怎样? [复制]

C++随笔:内存类型

Sidekiq Worker 缓存未过期

Sidekiq - 查看已完成的作业

ActiveJob + Sidekiq 6.0.3:如何写入日志文件?

工作中的 Sidekiq 重试计数