-
-
Save ryanlecompte/3281509 to your computer and use it in GitHub Desktop.
# 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 | |
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 .
Thank you @mboeh. That's very interesting and very good to know!
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.
Well, your second example seems to keep references to the threads. And IRB does keep a lot of stuff. On the other hand, if Ruby is keeping references to dead threads indefinitely, that's a problem for sure.