Skip to content

Instantly share code, notes, and snippets.

@ryanlecompte
Created August 7, 2012 04:16
Show Gist options
  • Save ryanlecompte/3281509 to your computer and use it in GitHub Desktop.
Save ryanlecompte/3281509 to your computer and use it in GitHub Desktop.
ActiveRecord memory leak with multiple threads?
# It appears that when I perform a query with AR via multiple threads,
# the instantiated objects do not get released when a GC is performed.
threads = Array.new(5) { Thread.new { Foo.where(:status => 2).all.first(100).each { |f| f.owner.first_name } } }
threads.each(&:join)
threads = nil
GC.start
ObjectSpace.each_object(Foo).count # => instances still exist
# ----------------
Foo.where(:status => 2).all.first(100).each { |f| f.owner.first_name }
GC.start
ObjectSpace.each_object(Foo).count # => 0
@mboeh
Copy link

mboeh commented Aug 7, 2012

I did my own testing and determined that setting threads to nil is insufficient to get the threads GC'd. You need to do threads.clear.

This is true even if you wrap the code creating the threads in a method, which is surprising -- I'd expect the local variable reference to be lost outside that method's scope, and the threads to be available for GC.

It seems that threads stored in an array in a local variable might not be available to GC when expected. I have a demonstration at https://gist.github.com/3287930 .

@ryanlecompte
Copy link
Author

Thank you @mboeh. That's very interesting and very good to know!

@raggi
Copy link

raggi commented Aug 7, 2012

Things in ObjectSpace are not necessarily live instances. There are shortcut tricks you can use to force memory to be free'd / overwritten (in MRI only, where it's full of hacks at the C level "for speed"). Assuming that any Ruby GC will be fully deterministic around GC.start behavior is unlikely to be productive.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment