如何在Linux上用Ruby 2.0改善独angular兽进程之间的内存共享
Ruby 2.0引入了一个写时复制友好的垃圾收集器。 我的进程似乎并没有让内存共享超过几分钟 – 似乎很快就会从shared_dirty转移到private_dirty。
一些其他人已经成功地获得这个工作:
- https://gist.github.com/kenn/5105175
- http://marianposaceanu.com/articles/on-ruby-2-0-memory-usage-unicorn-and-heroku
这个程序可以用来检查Linux上的内存统计信息: https : //gist.github.com/kenn/5105061
我的麒麟configuration: https : //gist.github.com/inspire22/f82c77c0a465f1945305
出于某种原因,我的独angular兽应用程序,preload_app = true,共享内存也less得多。 Ruby 2.0-p195,rails 3.2,linux 2.6.18(centos)
[root@thorn script]# ruby memstats.rb 4946 Process: 4946 Command Line: unicorn_rails worker[4] -c /u/apps/newap/current/lib/unicorn.rb -E production -D Memory Summary: private_clean 0 kB private_dirty 56,324 kB pss 60,256 kB rss 83,628 kB shared_clean 4,204 kB shared_dirty 23,100 kB size 108,156 kB swap 68 kB
如果我完全closures了主进程(不仅仅是一个HUP),然后重新启动它,并在任何请求排队之前立即检查一个工作者,我得到一个更好的故事:
[root@thorn script]# ruby memstats.rb 5743 Process: 5743 Command Line: unicorn_rails worker[4] -c /u/apps/newap/current/lib/unicorn.rb -E production -D Memory Summary: private_clean 0 kB private_dirty 21,572 kB pss 27,735 kB rss 66,296 kB shared_clean 2,484 kB shared_dirty 42,240 kB size 91,768 kB swap 0 kB
但在启动后的5秒钟内,他们又回到了〜20MB的shared_clean + shared_dirty。
我怀疑交换可能会导致这个问题,但是在降低swappiness并确保父进程和subprocess都没有交换(使用swapstats.rb)之后,问题依然存在。
我不明白什么shared_dirty内存,以及如何变成私人内存。 我也很乐意提高我的共享内存的寿命和数量。 谢谢!
根据你可能已经看到的这个答案 ,有一句话是这样写的:
请注意,“共享”页面被视为一个私有映射,直到它被实际共享。 即如果只有一个进程正在使用libfoo,则该库的文本部分将出现在进程的私有映射中。 只有当另一个进程开始使用该库时,才会将其logging在共享映射中(并从私有映射中删除)。
我会做什么来testing您是否获得了本文中概述的好处,将10MB xml文件作为文字string直接放入您的源代码中。 然后,如果您启动了20名工作人员,您将能够看到是否使用了200MB的内存,或者只有10MB,就像使用新的垃圾收集function所预期的那样。
更新:
我正在通过独angular兽的来源,并find了这个奇妙的文章的参考。
总而言之,为了适应您的应用程序,以便利用Ruby Enterprise Edition的写入时复制友好的垃圾回收器,您必须在分叉之前将GC.copy_on_write_friendly设置为true 。
if GC.respond_to?(:copy_on_write_friendly=) GC.copy_on_write_friendly = true end
根据您提供的独angular兽configuration文件,它似乎缺less分配。
另外,我喜欢阅读这些相关的文章:
- http://merbist.com/2011/02/22/concurrency-in-ruby-explained/
- http://linux.die.net/man/2/fork
- http://linux.die.net/man/2/clone
根据叉子手册页 :
在Linux下,fork()是通过使用写时复制页面来实现的,所以唯一的代价就是复制父页表所需的时间和内存,并且创build一个独特的子任务结构。
从版本2.3.3开始 ,不是调用内核的fork()系统调用,而是作为NPTL线程实现的一部分提供的glibc fork()包装调用克隆(2),其标志提供了与传统系统调用相同的效果。 (调用fork()相当于调用clone(2)指定标志,就像SIGCHLD一样。)glibc包装调用已经使用pthread_atfork(3)build立的任何fork处理程序。
根据克隆手册页 :
与fork(2)不同,这些调用允许subprocess与调用进程共享部分执行上下文,如内存空间,文件描述符表和信号处理程序表。
所以,我正在阅读这个意思:linux的fork fork-copy-on-write,这是独angular兽依赖于实现内存共享的function,直到libc 2.2.3才能实现(请问,如果我错了,有人纠正我在这个解释中)。
要检查您运行的libc版本,可以键入:
ldd --version
或者,findglibc并直接运行。 在我的系统上,它在以下位置find该文件:
locate libc.so /lib/x86_64-linux-gnu/libc.so.6