Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Garbarge Collection in Ruby with Circular Object References

I'm having an issue with garbage collection in Ruby where an object that I think should be garbage collection is not being garbage collected.

require 'ruby-mass'

def find_dependencies(_object_id,_mapped = {})
  mapped = _mapped
  points_to_object = Mass.references(Mass[_object_id])
  ids = points_to_object.keys.map{|x| /\#(\d*)/.match(x).captures.first.to_i}
  mapped[_object_id] = ids

  unmapped = ids - mapped.keys
  unmapped.each do |x|
    new_deps = find_dependencies(x,mapped)
    mapped.merge(new_deps)
  end
  mapped
end

Do some stuff that makes the objects, and find the relevant object ID. GC.start, then:

> find_dependencies(144789180)
=> {144789180=>[61895480, 144786340, 147807540],
 61895480=>[144789180],
 144786340=>[144789180],
 147807540=>[144789180]}

It looks like there is a circular reference pattern here, but it is all completely contained in these four objects, so the Mark-and-Sweep collector should find them and remove them.

So, either there is a bug in my find_dependencies_function, the Mass gem, or Ruby's garbage collector. How do I narrow this down to find out what the problem is and solve this memory leak?

like image 644
aaronjg Avatar asked Mar 29 '13 20:03

aaronjg


People also ask

Does garbage collector handle cyclic references?

yes Java Garbage collector handles circular-reference! How? There are special objects called called garbage-collection roots (GC roots). These are always reachable and so is any object that has them at its own root.

How does garbage collector resolves circular reference issue?

To handle the problem of circular references in C#, you should use garbage collection. It detects and collects circular references. The garbage collector begins with local and static and it marks each object that can be reached through their children. Through this, you can handle the issues with circular references.

Does Ruby use garbage collection?

The Ruby Garbage Collector module is an interface to Ruby's mark and sweep garbage collection mechanism. While it runs automatically in the background when needed, the GC module lets you call the GC manually whenever required and gain insights into how garbage collection cycles are running.

When a garbage collection starts it looks at a set of references called?

Chains of reference objects. A given path to an object can contain more than one kind of reference object, which creates a chain of reference objects. The garbage collector processes reference objects in order from strongest to weakest.


1 Answers

The GC in Ruby works essentially like so:

  1. Mark all global objects as live.

  2. Sweep through objects, garbage collect unless a parent is live.

So, in the case of a circular reference, A holding onto B holding onto A would get GC'd because neither is being held by a live object.

Per the comments, something is necessarily holding onto the object somewhere... Or maybe Mass is catching RangeError or something...

>> a = {}
=> {}
>> a[:a] = a
=> {:a=>{...}}
>> a.object_id
=> 2269556540
>> a = nil
=> nil
>> GC.start
=> nil
>> ObjectSpace._id2ref(2269556540)
RangeError: 0x8746af3c is recycled object
    from (irb):17:in `_id2ref'
    from (irb):17
like image 190
Denis de Bernardy Avatar answered Oct 30 '22 15:10

Denis de Bernardy