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

时间:2012-11-14 21:30:11

标签: ruby-on-rails ruby performance garbage-collection latency

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

我的Ruby版本是:

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

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

enter image description here

4 个答案:

答案 0 :(得分:10)

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

RUBY_HEAP_MIN_SLOTS=800000
RUBY_FREE_MIN=100000
RUBY_GC_MALLOC_LIMIT=79000000

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

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

这些参数here和更多信息here已完整写入,虽然这些帖子是为REE 1.8.7编写的,但它们也适用于Ruby 1.9.2+。 / p>

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

答案 1 :(得分:0)

您应该使用分配跟踪器来查找代码分配对象的位置以及分配对象的数量。我过去使用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. 避免在多次执行的循环中使用字符串常量(或数组/散列文字)(每次迭代都会分配一个新对象)。

答案 2 :(得分:0)

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

答案 3 :(得分:0)

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

这个技巧可能会或可能不会满足您的需求。

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