Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby GC execution exceeding ~250-320ms per request

I have a ruby on rails application. I am investigating an Apdex decline in my NewRelic portal and I'm seeing that on average, 250-320ms of time is being spent on GC execution. This is a highly disturbing number. I've included a screen shot below.

My Ruby version is:

ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]

Any suggestions for tuning this would be ideal. This number should be substantially lower.

enter image description here

like image 360
randombits Avatar asked Nov 14 '12 21:11

randombits


People also ask

What is the use of compaction in Ruby?

This function compacts objects together in Ruby's heap. It eliminates unused space (or fragmentation) in the heap by moving objects in to that unused space. This function returns a hash which contains statistics about which objects were moved. See `GC.latest_gc_info` for details about compaction statistics.

What is the use of the GC module in Ruby?

The GC module provides an interface to Ruby's mark and sweep garbage collection mechanism. Some of the underlying methods are also available via the ObjectSpace module. You may obtain information about the operation of the GC through GC::Profiler. Raises NoMemoryError when allocating an instance of the given classes.

What is “minor GC” in Ruby?

This is based on the theory that most objects are used briefly and then aren’t needed anymore. With generational garbage collection, Ruby maintains separate object spaces for “young” and “old” objects. Then it goes through only the young spaces most of the time. This is the “minor GC” phase.

What is GC start command in Ruby?

GC.start is another good Ruby garbage collection command to know. It kicks off a garbage collection cycle. If you have a long-running script that’s chewing up memory, you can have it call GC.start periodically to keep the memory usage under control.


1 Answers

You're spending so much time in GC because you're running your GC so often. Ruby, by default, sets GC parameters that are appropriate for small scripts, not large apps. Try launching your app with the following environment parameters set:

RUBY_HEAP_MIN_SLOTS=800000
RUBY_FREE_MIN=100000
RUBY_GC_MALLOC_LIMIT=79000000

What this'll do is increase the initial heap allocation size and pad the GC numbers so that it doesn't run quite so often. This may let your app use a bit more RAM, but it should reduce the time spent in GC dramatically. Under the default settings, you're likely running GC multiple times per request; you want to be ideally be running it once every few requests (or even better, between requests with something like Unicorn's OOB::GC).

Those are my GC settings for my app, and you'll want to tweak them up and down as is most appropriate for your app to find the right settings; you're gunning for a middle ground where you aren't running GC so often, and don't have excessive memory usage. This is specific to each app, though, so there's no boilerplate advice I can give on what those exact settings should be. Increasing from the defaults (10k slots, 1.8x growth factor) should have an immediate impact, and you can tweak up and down from there as best fits your current situtation.

There's a full writup of these parameters here and more information here, and while those posts were written for REE 1.8.7, they're applicable to Ruby 1.9.2+ as well.

Those are some rather extreme numbers, so it's possible that you're doing something in your app that is causing you to allocate much more RAM than you should, so I'd encourage you to be suspicious and comb through your app looking for over-eager allocations. The GC environment variables should help triage the situation in any case, though.

like image 198
Chris Heald Avatar answered Oct 15 '22 05:10

Chris Heald