Mil*_*vic 13 ruby memory memory-leaks memory-management
我有一个工作进程,它产生多达50个线程并执行一些异步操作(大多数是http调用).当我启动该过程时,它从大约35MB的已用内存开始,并迅速增长到250MB.从那时起它进一步增长,问题是内存永远不会停止增长(即使增长阶段随着时间的推移而减少).几天后,进程只会超出可用内存并崩溃.
我做了很多分析和分析,似乎无法找到问题所在.即使堆大小非常不变,进程内存也在不断增长.我已将GC.stat输出收集到您可以在此处访问的电子表格中:
https://docs.google.com/spreadsheets/d/17TohDNXQ_MXM31CeAmR2ptHFYfvOeF3dB6WCBkBS_Bc/edit?usp=sharing
即使过程内存最终稳定在415MB,但它将在接下来的几天内持续增长,直到达到512MB的限制并崩溃.
我也尝试使用对象空间跟踪对象,但跟踪对象的内存总和从不超过70-80MB,这与GC报告完全一致.剩余的300MB +(和不断增长的)花在哪里..我不知道.
如何处理这类问题?是否有任何工具可以让我更清楚地了解内存的使用方式?
更新:宝石和操作系统
我正在使用以下宝石:
gem "require_all", "~> 1.3"
gem "thread", "~> 0.1"
gem "equalizer", "~> 0.0.9"
gem "digest-murmurhash", "~> 0.3", require: "digest/murmurhash"
gem "google-api-client", "~> 0.7", require: "google/api_client"
gem "aws-sdk", "~> 1.44"
Run Code Online (Sandbox Code Playgroud)
该应用程序部署在heroku上,但在Mac OS X 10.9.4上本地运行时,内存泄漏是显而易见的.
更新:泄漏
我已经升级了stringbuffer并分析了所有像@mtm建议的内容,现在没有leak工具识别的内存泄漏,随着时间的推移没有增加ruby堆大小,然而,进程内存仍在增长.最初我认为它在某些时候停止了增长,但几个小时之后它就超过了极限并且过程崩溃了.
Mat*_*att 18
从GC日志中看,问题不是ruby对象引用泄漏,因为heap_live_slot值没有显着增加.这表明问题是:
值得注意的是,问题出现在OSX和Heroku(Ubuntu Linux)上.
Ruby 2.1垃圾收集仅对包含少量数据的对象使用报告的"堆".当Object中包含的数据超过某个限制时,数据将被移动并分配给堆外部的区域.您可以使用ObjectSpace获取每种数据类型的总体大小:
require 'objspace'
ObjectSpace.count_objects_size({})
Run Code Online (Sandbox Code Playgroud)
将此与GC统计信息一起收集可能会指示在堆外部分配内存的位置.如果你找到一种特定的类型,:T_ARRAY比起其他类型的增加,你可能需要寻找一个你永远追加的数组.
您可以使用pry-byebug放入控制台来绕过特定对象,甚至可以查看根目录中的所有对象:
ObjectSpace.memsize_of(some_object)
ObjectSpace.reachable_objects_from_root
Run Code Online (Sandbox Code Playgroud)
关于ruby开发者博客之一以及SO回答的更多细节.我喜欢他们的JRuby/VisualVM分析想法.
使用bundle你的宝石安装到本地路径:
bundle install --path=.gems/
Run Code Online (Sandbox Code Playgroud)
然后你可以找到包含本机代码的那些:
find .gems/ -name "*.c"
Run Code Online (Sandbox Code Playgroud)
这给了你:(按我的怀疑顺序)
OSX有一个有用的开发工具leaks,可以告诉你它是否在正在运行的进程中找到未引用的内存.对于识别内存来自Ruby的位置并不是很有用,但有助于识别它何时发生.
首先要测试的是digest-stringbuffer.从自述文件中获取示例,并使用gc_tracer添加一些GC日志记录
require "digest/stringbuffer"
require "gc_tracer"
GC::Tracer.start_logging "gclog.txt"
module Digest
class Prime31 < StringBuffer
def initialize
@prime = 31
end
def finish
result = 0
buffer.unpack("C*").each do |c|
result += (c * @prime)
end
[result & 0xffffffff].pack("N")
end
end
end
Run Code Online (Sandbox Code Playgroud)
让它运行很多
while true do
a=[]
500.times do |i|
a.push Digest::Prime31.hexdigest( "abc" * (1000 + i) )
end
sleep 1
end
Run Code Online (Sandbox Code Playgroud)
运行示例:
bundle exec ruby ./stringbuffertest.rb &
pid=$!
Run Code Online (Sandbox Code Playgroud)
监视ruby进程的驻留和虚拟内存大小,以及已leaks识别的计数:
while true; do
ps=$(ps -o rss,vsz -p $pid | tail +2)
leaks=$(leaks $pid | grep -c Leak)
echo "$(date) m[$ps] l[$leaks]"
sleep 15
done
Run Code Online (Sandbox Code Playgroud)
看起来我们已经找到了一些东西:
Tue 26 Aug 2014 18:22:36 BST m[104776 2538288] l[8229]
Tue 26 Aug 2014 18:22:51 BST m[110524 2547504] l[13657]
Tue 26 Aug 2014 18:23:07 BST m[113716 2547504] l[19656]
Tue 26 Aug 2014 18:23:22 BST m[113924 2547504] l[25454]
Tue 26 Aug 2014 18:23:38 BST m[113988 2547504] l[30722]
Run Code Online (Sandbox Code Playgroud)
驻留内存正在增加,泄漏工具正在发现越来越多的未引用内存.确认GC堆大小,对象计数仍然稳定
tail -f gclog.txt | awk '{ print $1, $3, $4, $7, $13 }
1581853040832 468 183 39171 3247996
1581859846164 468 183 33190 3247996
1584677954974 469 183 39088 3254580
1584678531598 469 183 39088 3254580
1584687986226 469 183 33824 3254580
1587512759786 470 183 39643 3261058
1587513449256 470 183 39643 3261058
1587521726010 470 183 34470 3261058
Run Code Online (Sandbox Code Playgroud)
然后报告问题.
这似乎很我ç未经训练的眼睛,他们都分配一个指针和一个缓冲,但只清理了缓冲区.
看着digest-murmurhash,似乎只提供依靠StringBuffer的功能,所以一旦StringBuffer的固定泄漏可能会被罚款.
当他们修补它时,再次测试并移动到下一个宝石.对于每个gem测试而不是通用示例,最好使用实现中的代码片段.
第一步是在相同的MRI下在多台机器上证明问题,以排除您已经完成的任何本地操作.
然后在不同的操作系统上尝试相同的Ruby版本,您也已经完成了.
如果可能的话,尝试使用JRuby或Rubinius上的代码.是否会出现同样的问题?
如果可能,请尝试在2.0或1.9上使用相同的代码,看看是否存在相同的问题.
尝试从github的头开发版本,看看是否有任何区别.
如果没有什么变得明显,请向 Ruby 提交一个错误,详细说明问题以及您已消除的所有内容.等待开发人员帮忙并提供他们需要的任何东西.如果您能够获得问题设置的最简洁/最简单的示例,他们很可能想要重现该问题.这样做通常可以帮助您确定问题所在.
| 归档时间: |
|
| 查看次数: |
2717 次 |
| 最近记录: |