Ruby's garbage collector is garbage. There are many tips online for improving Ruby on Rails (RoR) performance by patching the stock garbage collector and compiling your own ruby interpreter. This is a pain in the ass if you are managing a bunch of boxes.

One method to help things that I applied at work recently was adding this to my app/controllers/application_controller.rb file. I got an improvement in execution time of a particularly tough action from about 750 ms down to 200 ms using this technique. With that said, I felt very dirty applying it. It is definitely a "Rob Peter to pay Paul" moment and I wonder where the time I robbed is going to come back and and bite me in the ass.

class ApplicationController
  around_filter :no_gc
  def no_gc
    GC.disable
    begin
      yield
    ensure
      GC.enable
    end
  end
end

I don't recommend this in practice. I have a feeling the app will suffer performance burps every once in a while as accumulated garbage is collected every N requests. But so far, it seems to be working in a staging environment. Time will tell ...

9/18/2009: Rather than hack around to disguise a problem, analyze the reason for the problem ...

After modifying the above around filter as follows ...

class ApplicationController
  around_filter :no_gc
  def no_gc
    GC.disable
    begin
      start_count = ObjectSpace.each_object {}
      yield
      end_count = ObjectSpace.each_object {}
      logger.info("OBJECTS CREATED = #{end_count - start_count}")
    ensure
      GC.enable
    end
  end
end

I have determined that the action I am profiling is creating a boatload of objects (89K+ per request!!). No wonder we are having GC issues. By turning off the garbage collector, we put a temporary band aid on a real problem. My initial instincts about this solution were correct: it smells. We still haven't figured out where all the object creations are coming from, but now that we know what to look for, it shouldn't be too hard to find them.