Ruby GC执行超过每个请求约250-320ms

我在rails应用程序上有一个ruby。 我正在调查我的NewRelic门户网站的Apdex下降,我看到平均而言,GC执行花费了250-320ms。 这是一个非常令人不安的数字。 我在下面放了一个屏幕截图。

我的Ruby版本是:

ruby 1.9.3p194(2012-04-20修订版35410)[x86_64-linux]

任何调整这个的建议都是理想的。 这个数字应该大大降低。

在此处输入图像描述

你在GC上花了那么多时间,因为你经常运行你的GC。 默认情况下,Ruby设置适用于小脚本的GC参数,而不是大型应用程序。 尝试使用以下环境参数设置启动您的应用:

 RUBY_HEAP_MIN_SLOTS=800000 RUBY_FREE_MIN=100000 RUBY_GC_MALLOC_LIMIT=79000000 

这样做会增加初始堆分配大小并填充GC编号,使其不会经常运行。 这可能会让您的应用程序使用更多的RAM,但它应该大大减少在GC中花费的时间。 在默认设置下,您可能每次请求多次运行GC; 你希望理想情况下每隔几个请求运行一次(甚至更好,在使用Unicorn的OOB :: GC之类的请求之间)。

这些是我的应用程序的GC设置,您需要上下调整它们,以便最适合您的应用程序找到正确的设置; 你正在寻找一个你不经常运行GC的中间地带,并且没有过多的内存使用量。 这是特定于每个应用程序,所以没有样板建议我可以给出这些确切的设置应该是什么。 从默认值(10k插槽,1.8倍增长因子)增加应立即产生影响,并且您可以从那里进行调整,以最适合您当前的情况。

这里有完整的这些参数和更多信息,虽然这些post是为REE 1.8.7编写的,但它们也适用于Ruby 1.9.2+。

这些是一些相当极端的数字,所以你可能在你的应用程序中做了一些事情,导致你分配比你应该多得多的内存,所以我鼓励你怀疑并梳理你的应用程序寻找过度 – 急切的分配。 但是,GC环境变量应该有助于在任何情况下对情况进行分类。

您应该使用分配跟踪器来查找代码分配对象的位置以及分配对象的数量。 我在过去使用过memprof并取得了很好的效果……最大的缺点是它只能在Ruby 1.8下工作(希望你的代码是1.8.7兼容的)。

如果你可以在Ruby 1.8.7下运行你的应用程序,那么安装“memprof”gem,并且:

 require 'memprof' GC.disable Memprof.track { run_test_code_here } 

这将打印分配的对象计数列表(按类和按发生分配的源行分组)到标准输出。

当您在垃圾收集器中花费过多时间时遇到问题,通常分配跟踪会显示您的程序分配大量对象的一个​​或两个位置。 事先不可能说解决方案是什么,但通常会涉及:

  1. 缓存结果以避免重复计算,
  2. 使用破坏性操作,这样做是安全的(如map!而不是map ),
  3. 在循环中重用临时对象(每次重置其状态),而不是在每次迭代时分配一个新对象,或者
  4. 避免在循环中使用字符串常量(或数组/散列文字)多次执行(每次迭代都会分配一个新对象)。

还有这个小黑客 ,可能会有效。 但是更大的应用程序倾向于分配如此大量的内存,以至于每个请求都会杀死一个工作者: – /

假设您没有错误地创建不需要的对象我听说一个hack /解决方案(除了使用JRuby)是在完成发送响应后强制GC。 通过这种方式,您可以获得较大的暂停,但请求的消费者不会看到它。 如果你有这么多垃圾,你会看到这样的多次停顿,那么你可能会失去运气。

这个技巧可能适用于您的需求,也可能不适用。

-JRuby家伙与MRI GC问题的人交谈:)