39

Ruby 2.0 引入了一种写时复制友好的垃圾收集器。我的进程似乎并没有保持共享内存超过几分钟 - 它似乎很快从 shared_dirty 移动到 private_dirty。

其他一些人已经成功地让它发挥作用:

该程序可用于检查 Linux 上的内存统计信息:https ://gist.github.com/kenn/5105061

我的独角兽配置:https ://gist.github.com/inspire22/f82c77c0a465f1945305

出于某种原因,我的独角兽应用程序(也具有 preload_app=true)的共享内存要少得多。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 

如果我完全关闭主进程(不仅仅是 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。

我怀疑交换可能会导致问题,但是在降低交换性并确保父进程和子进程都没有被换出(使用 swapstats.rb)之后,问题仍然存在。

我不明白 shared_dirty 内存是什么,以及它是如何变成私有内存的。我也喜欢关于提高共享内存的寿命和数量的建议。谢谢!

4

1 回答 1

7

根据您可能已经看到的这个答案,有一行内容如下:

请注意,在实际共享之前,“可共享”页面被视为私有映射。即,如果当前只有一个进程在使用 libfoo,则该库的文本部分将出现在进程的私有映射中。仅当/当另一个进程开始使用该库时,它才会在共享映射中计算(并从私有映射中删除)。

为了测试您是否获得了本文中概述的好处,我要做的就是将一个 10MB 的 xml 文件作为文字字符串直接放入您的源代码中。然后,如果您启动 20 个工作人员,您将能够查看您是否使用了 200MB 的内存,或者仅使用了 10MB,这与新的垃圾收集功能所预期的一样。

更新:

我正在查看unicorn 的源代码,并找到了对这篇精彩文章的引用。

总而言之,它指出,为了使您的应用程序能够利用 Ruby 企业版的写时复制友好垃圾收集器,您必须在 fork 之前将 GC.copy_on_write_friendly 设置为 true

if GC.respond_to?(:copy_on_write_friendly=)
    GC.copy_on_write_friendly = true
end

根据您提供的独角兽配置文件​​,它似乎缺少分配。

此外,我喜欢阅读这些相关文章:

根据fork 手册页

在 Linux 下,fork() 是使用写时复制页实现的,因此它所招致的唯一损失是复制父页表并为子页创建独特的任务结构所需的时间和内存。

从版本 2.3.3开始,作为 NPTL 线程实现的一部分提供的 glibc fork() 包装器不再调用内核的 fork() 系统调用,而是调用具有与传统系统调用相同效果的标志的 clone(2) . (对 fork() 的调用等同于对 clone(2) 的调用,将标志指定为 SIGCHLD。)glibc 包装器调用任何使用 pthread_atfork(3) 建立的 fork 处理程序。

并根据克隆手册页

与 fork(2) 不同,这些调用允许子进程与调用进程共享其部分执行上下文,例如内存空间、文件描述符表和信号处理程序表。

所以,我读这个的意思是:linux的fork copy-on-write,这是unicorn依赖于实现内存共享的特性,直到libc 2.2.3才实现(如果我错了,请有人纠正我在这个解释中)。

要检查您正在运行的 libc 版本,您可以键入:

ldd --version

或者,找到 glibc 并直接运行它。在我的系统上,它在以下位置找到了该文件:

locate libc.so
/lib/x86_64-linux-gnu/libc.so.6
于 2014-01-05T03:07:38.057 回答