最近我的一个 ruby on rails 的项目,部署采用的是(nginx+passenger),由于访问量大概增了 6 倍左右,出现了一个之前没有注意到的问题,就是每个 ruby 进程的内存一直不断地增长,有一次跑了 3 天没管,多个 ruby 进程从刚开始的 100 多 M 到达 500M 左右,不得已每天晚上 5 点左右重启一下服务以便释放内存。换了 ruby 2.0 有所改善,但是还是不明显。
跑了 11 几个小时左右
跑了 80 几个小时左右
在网上看到了Robbin 的这篇文章后,在生产环境选了一台机器观察下,我发现从来没有内存使用有降低的情况。于是开始猜测是不是没有触发 GC,我的 ruby gc 参数参考 37singals 和 twitter 的 配置如下:
export RUBY_HEAP_MIN_SLOTS=1250000
export RUBY_HEAP_SLOTS_INCREMENT=100000
export RUBY_HEAP_SLOTS_GROWTH_FACTOR=1
export RUBY_GC_MALLOC_LIMIT=30000000
export RUBY_HEAP_FREE_MIN=12500
用 GC::Profiler
去观察 ruby 的 GC 情况,得到结果如下:
明显很多次自动触发了 GC,后面我自己强制调用 GC.start,Use Size 和 Total Size 都降低了很多,但是该 ruby 进程的内存占用量始终不降。(中间我观察有几次次非常小幅的下降----每次大概 1% 约 40m,但总体在增长)
当执行完 GC 后,如果在改 ruby 进程里面再次大量的分配对象时,整体内存增长不大,于是我猜测 ruby 所谓的 GC 是不是只是进程内部把一使用的内存回收以便下次再次分配时使用,但是没有释放给系统?求大神指点。
25729 是我 rails consle 起来后 跑 2 楼那个测试代码,不断地释放 a 和 再分配 a 最终结果。 其他进程就是我用 passenger 起来的进程
然后 25729 就从 100 多的 m 涨到了 2G
virt 不是真正使用的内存可以不管。res (resident) 准确一点。
会释放给系统的
可能是你的代码里有内存泄漏。
不过 lazy sweep 机制使得 gc 完以后,分配内存时,才去 sweep, 而且 sweep 到能分配的程度后,就不再 sweep 了。你可以试试 GC.stress = true
(但会变慢很多,弄完记得 GC.stress = false
)
32 位机器的保守式 gc 扫栈也会导致一些泄漏,不过 64 位机器几乎不会。
ruby gc 没有 compact 机制,长时间运行内存碎片增多也会导致占用内存增多,但不明显。
中间产生大量临时对象的话是有可能出现这种情况的。GC 只是把没用的对象放回 free list.
ruby 是通过多个 heaps 来管理内存的,每个 heap 有许多 slot 用来存放对象,只有当一个 heap 的 slot 全部是 free 的时候 ruby 才会把这个 heap 释放掉,把内存交还 os.
建议做 memory profile, 看看什么地方产生的对象很多。
我的一个项目也是用 Nginx+Passenger,每个进程启动占用 100M,之后内存也会涨一些,但是不会释放。我采取的策略是,设置 PassengerMaxRequests 参数,在处理指定数量的请求后会自动重启。因为我用的是免费版,付费版还可以设置内存到 Max 值自动重启。
Test Ruby's ability to release memory back to system. (1.9.3 vs 2.0) https://gist.github.com/rjackson/5071864
我以为 MRI Ruby runtime 本来就不会把用不到的记忆体释回给 OS:http://stackoverflow.com/questions/5968992/ruby-hash-memory-leak-after-key-deletion
可以换成 unicorn 看看,在内存达到设定的的条件时,该 ruby 进程就会自杀,然后会重新启动一个新的 ruby 进程,这样解决了 ruby 内存不断上涨并且不释放内存的问题 http://ruby-china.org/topics/12033