【问题标题】:How to improve memory sharing between unicorn processes with Ruby 2.0 on Linux如何在 Linux 上使用 Ruby 2.0 改善独角兽进程之间的内存共享
【发布时间】:2013-06-15 08:55:59
【问题描述】:

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 内存,以及它是如何变成私有内存的。我也喜欢关于提高共享内存的寿命和数量的建议。谢谢!

【问题讨论】:

  • 我仍然没有解决方案。我现在认为这是 a) 32 位 linux 版本的问题,b) 内存受限的环境(尽管即使关闭了 swappiness,它仍然会发生)
  • 我从 gist 的作者那里得到了这条评论:twitter.com/kenn/status/402832587007086592
  • 我也ping了其他东西的作者:twitter.com/dakull/status/403156502598279170
  • 出于好奇,如果您尝试对 Phusion Passenger 进行同样的操作,效果如何?
  • 那么,Ruby 2.1 有什么不同吗?

标签: ruby-on-rails linux memory unicorn ruby-2.0


【解决方案1】:

根据您可能已经看到的this answer,有一行内容如下:

请注意,“可共享”页面被视为私有映射,直到它 实际上是共享的。即如果当前只有一个进程在使用 libfoo,该库的文本部分将出现在进程的 私有映射。它将计入共享映射(和 从私有进程中删除)仅当/当另一个进程启动时 使用那个库。

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

更新:

我在查看the unicorn source 并找到了对this wonderful article 的引用。

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

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

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

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

根据fork man page

在 Linux 下,fork() 是使用写时复制页面实现的,所以 它招致的唯一惩罚是时间和内存需要 复制父级的页表,并创建一个独特的任务 孩子的结构。

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

并根据clone man page

与 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

【讨论】:

  • 有趣的测试想法,谢谢!从理论上讲,我上面链接的 memstats.rb 程序应该解释这一点,但你永远不知道。
  • 我看的越多,这是一个相当糟糕的问题,因为它是“开放式的”。如果不访问系统,就不可能“确定”答案。您的回答提供了一种很好的方法来进一步调试可能出现的问题。最终,我怀疑这与 32 位系统上的旧版本 linux 有关,具有一些轻微的内存限制/交换。您的测试是个好主意,尽管它可能会与 memstats.rb 脚本重复。
  • @Kevin 添加了似乎是您的问题原因的更新。请让我知道它是否适合您。
  • @cutalion :-) 很高兴为您提供帮助。请告诉我们你是怎么做出来的。这是否为您解决了问题?
  • GC.copy_on_write_friendly 仅适用于 REE 或 ruby​​ 1.x - ruby​​ 2.0 默认启用它。我的glibc是2.5,仅供参考。
猜你喜欢
  • 2015-03-12
  • 2014-05-08
  • 2021-03-26
  • 2012-01-11
  • 1970-01-01
  • 2019-11-28
  • 2013-07-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多