I have a memory leak in a Rails 4.2.6 application. A controller allocates a large GaragesPresenter
object as an instance variable, which should be de-referenced and garbage collected after the request completes. However, I see that this never happens.
def show@garage = GaragesPresenter.new(@garage, view_context)respond_to do |format|format.htmlendend
I see that a reference to the GaragesPresenter
instance is being held by the GaragesController
instance, and an instance to that is being held by the GaragesController
class. This is true long after the request has completed and GC.start
has been called. Why is the GaragesController
class holding a reference to the instance?
I know this because I set up a heap dump with:
require 'objspace'...GC.startfile = File.open("/tmp/dumpfile", 'w')ObjectSpace.dump_all(output: file)
And in the resulting file I see the following three objects:
The following object is a GaragesPresenter, which is very large:
{"address":"0x7fd077217e20", "type":"OBJECT", "class":"0x7fd074a04618", "ivars":7, "references":["0x7fd0772bf940", "0x7fd077711480", "0x7fd077748188", "0x7fd077772898", "0x7fd07720c778", "0x7fd0771ef8d0", "0x7fd0771ef8d0"], "file":"/Users/dyoung/workspace/commutyble/site-app/app/controllers/garages_controller.rb", "line":19, "method":"new", "generation":35, "memsize":56, "flags":{"wb_protected":true, "old":true, "marked":true}}
A reference to the above object is being held by a GaragesController instance (expected, as the show method allocates the presenter as an instance variable):
{"address":"0x7fd0727559f0", "type":"OBJECT", "class":"0x7fd0727865a0", "ivars":22, "references":["0x7fd0727558b0", "0x7fd072755888", "0x7fd072755838", "0x7fd0732400e0", "0x7fd072754a50", "0x7fd0734c5658", "0x7fd07704e878", "0x7fd0732ab020", "0x7fd072785ee8", "0x7fd077217e20", "0x7fd0771ffe10", "0x7fd07720cde0", "0x7fd0732a82d0"], "file":"/Users/dyoung/.rvm/gems/ruby-2.1.0/gems/actionpack-4.2.6/lib/action_controller/metal.rb", "line":237, "method":"new", "generation":35, "memsize":176, "flags":{"wb_protected":true, "old":true, "marked":true}}
A reference to the above GaragesController instance is being held by the GaragesController class, presumably preventing garabage collection. Why??
{"address":"0x7fd0727865a0", "type":"CLASS", "class":"0x7fd0726a7260", "name":"GaragesController", "references":["0x7fd0727559f0", "0x7fd0726a72b0"], "file":"/Users/dyoung/.rvm/gems/ruby-2.1.0/gems/activesupport-4.2.6/lib/active_support/callbacks.rb", "line":435, "method":"instance_exec", "generation":35, "memsize":672, "flags":{"wb_protected":true, "old":true, "marked":true}}
Best Answer
GaragesPresenter
holds a reference to view_context
@garage = GaragesPresenter.new(@garage, view_context)
view_context
returns an instance of of a view class which holds a reference to self
, which is the invoking controller:
# File actionview/lib/action_view/rendering.rb, line 71def view_contextview_context_class.new(view_renderer, view_assigns, self)end
You need to use WeakRef
Weak Reference class that allows a referenced object to begarbage-collected. A WeakRef may be used exactly like the object itreferences.
foo = Object.newfoo = WeakRef.new(foo) # Creates a weak reference to origObjectSpace.garbage_collectp foo.to_s # should raise exception (recycled)
The use case is there where two object reference is used. First is master and second is weak. Your object will not be garbage collected until first master link is in use. Use master link (generic variable) in the object which live same or more time than we need referenced object. And inside of referenced object use weak link.
It is corresponding practice for this case. In another languages with garbage collector too, in perl for example. The C++ libraries have too much solutions for memory management strategies. Garbage collector can not remove garbage (objects) when they in use. If object refer to another and another refer to first this means both in use. So it is not garbage - it is useful for "garbage collector opinion". But really it is garbage - and it is memory leak.
Graph of objects-references must not have loops or cycles. If we need reference that forms loop or cycle in objects-references graph we need to use at least one quasi cutting weak reference in each.