Sidekiq在工作完成后没有释放内存

33

我有大约六个 Sidekiq worker 执行JSON 爬取。根据端点的数据集大小,它们需要1分钟到4小时完成。尤其是那个需要4小时的长时间任务,我发现内存会随着时间的推移略微增加。

这不是问题,直到我想再次调度同样的 worker 任务时。内存没有被释放并且堆积,直到我遇到 Linux OOM Killer,它会摆脱我的 Sidekiq 进程。

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

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

实际上并没有增加,哈希、数组等集合保持不变,短暂的增加会被垃圾回收器清除掉,gc.stat[:count] 告诉我,垃圾回收器也在工作。

即使工作线程结束,例如我得到了打印的[完成]且没有工作线程了,内存仍然没有被释放。造成这种情况的原因是什么?我能做些什么来解决吗?编写一个终结器吗?

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

我使用Yajl进行 JSON 解析,它是一个 C 绑定。我需要它,因为它似乎是唯一一个适当实现流式读写的快速 JSON 解析器。


1
你使用哪个 gem 来解析输入的 JSON?你是否使用了其他带有 C 扩展的 gem?从你的描述中可以看出(内存使用量增加,但 Ruby 对象数量不变),你可能会遇到来自带有 C 扩展的 gem 的泄漏问题(例如,某些 gem 分配了未用于存储 Ruby 对象的内存,并且永远不会释放它)。 - grumbler
4
也有可能你在纯 Ruby 代码中存在“泄漏”,即重复地修改对象并导致其大小增大而不分配新对象。例如,反复追加一个 Ruby 字符串会导致它不断消耗更多内存,而不增加你的对象计数。 - grumbler
1
@grumbler 哦,好主意。我扩展了我的问题。我使用Yajl进行JSON解析,它确实是一个C绑定。从来没有想过这一点。 - Guarana Joe
2
如果我理解正确的话,Ruby不会释放它曾经保留过的内存,即使在垃圾回收期间也不会。垃圾回收所“释放”的内存实际上只是被标记为可以由同一Ruby进程安全重用的内存。此外,如果您的JSON解析器将JSON转换为带有符号键的哈希表,那么这些符号中的任何一个都永远不会被垃圾回收,因为符号是永久的。 - Kimmo Lehto
1
自 Ruby 2.2 版本开始,符号已经实现了垃圾回收功能:https://www.ruby-lang.org/en/news/2014/12/25/ruby-2-2-0-released/此外,Ruby 实际上将内存释放回操作系统:https://github.com/ruby/ruby/blob/d94ea30a576136d93b122b6eb69971b18be927d5/gc.c#L1459-L1462 - Psylone
显示剩余4条评论
1个回答

13

1
好的指针 @digitalextremist +1 - Rohit
11
老帖子了,但想知道您是否知道这是否仍适用于Ruby 2.1及其新的GC? - Craig Knox
我们仍然面临着同样的问题,即MRI不会释放内存。 - Hendrik
2
同样,MRI不会释放内存。这是一个关键问题,我希望我早些知道它。 - sidney
Ruby Enterprise Edition已经停止维护,它基于Ruby 1.8.7,甚至不建议使用。我目前正在尝试将我的应用程序转换为JRuby。 - sidney

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接