2

My colleagues and I are in the midst of hunting down a memory leak in our Ruby on Rails application and we have some initial indicators that code resembling the following may be the culprit:

module Foo
  class Bar
    def self.example
      @widgets ||= ::Widget::Factory.new
    end
  end
end

Our hunch is that because self.example is a class method, it may not be properly garbage collecting the memoized instance of Widget::Factory, which utilizes extensive caching itself.

This, we believe, is leading to a memory leak every time one of our background workers is run. It seems to be spinning up an instance of Foo::Bar but never deallocating the objects created by Widget::Factory.

Does anyone have any insight into how MRI's GC would work for such a use-case or is this just a red-herring?

doremi
  • 14,921
  • 30
  • 93
  • 148
  • 1
    My understanding is that objects referenced by constants like `Foo::Bar` are global and thus always reachable and never garbage collected. If you have other objects attached to `Foo::Bar`, such as the memoized `@widgets` in your example, then those objects won't be GC'd either. – Matt Brictson Sep 23 '16 at 16:50
  • @MattBrictson do we know if `Foo::Bar.example` is called multiple times in the same proc if it re-uses the same objects? If so, that would rule out this as the culprit as there would only ever be one set of objects allocated in the global space. – doremi Sep 23 '16 at 16:55
  • 1
    Yes, it re-uses the same objects. You can verify that with `puts @widgets.object_id`. – Matt Brictson Sep 23 '16 at 17:21
  • you can add some log/metric for evidence – max pleaner Sep 23 '16 at 18:34
  • I think you should ask this on one of the ruby-mailing lists, where the core-team-hackers hang around. – Felix Sep 24 '16 at 00:16
  • 1
    As written, I wouldn't expect this to leak in the normal sense. Certainly, `@widgets` will not be garbage collected, but, it will also never grow in size. My understanding of the term 'memory leak' implies at least the _possiblity_ that the amount of 'wasted' memory goes up over time, whereas the code as written would only ever 'leak' a single item over the lifetime of the process. Of course, if the `@widgets` object is a collection, or references a collection, and other code not shown in the example causes the collection to grow over time, _that_ would be a leak. – philomory Sep 24 '16 at 01:19
  • @philomory Thank you for your response. You are exactly right. Today, my colleague discovered that deep within `Widget::Factory`, an object was being appended to a cache on each call. Was a tough find, but there it was in a single line of about 20 characters. – doremi Sep 24 '16 at 03:39

0 Answers0