So, there are a couple of issues with the old approach and resource leaks. Let's take a look at the behaviour we would like, the problems we could see with the old approach, and what we could do to fix those. We'll start by considering a simple case of three objects A, B, and C. A will be a GC root (i.e. it's either some sort of global object or a variable which the garbage collector knows cannot be removed), and B and C are Ruby objects. A will have an instance variable of B, and B will have one of C. So A has a hard reference to B and B has a hard reference to C like this.

Sorry, your browser does not support SVG.

We'll use red arrows to indicate hard references from now on, and red boxes to indicate GC roots. The good thing here is that if A stops being a root, or a hard reference is removed then some or all of the objects can be collected.

Sorry, your browser does not support SVG.

Now, what happens when A, B, and C are C structs which have been wrapped in Ruby objects? Well, now our hard references are just VALUEs in the structs, and for the GC to know those are alive each object must mark those it has references to. We'll use purple to mark marked links.

Sorry, your browser does not support SVG.

So in this situation if A stops marking B, or B stops marking C, or A ceases to be a GC root then those objects can be collected, just as they were above.

Now what happens in TruffleRuby when we use handles?

Sorry, your browser does not support SVG.

Now we have a problem. The references from A to B, and B to C are weak (they are simple handle numbers) so the handle table has to associate those with the objects they represent by keeping a strong reference. For B or C to be garbage collected we would need to remove the handle pointing to them. Likewise if A ceases to be a root something needs to happen for B and C to be collected.

We might be able to do that with a finaliser for A which in turn releases the handle to B, whose finaliser can release the handle to C, but the situation may not always be so simple. Consider the following structure of objects.

Sorry, your browser does not support SVG.

This is a common sort of structure to find in tools like XML processors such as Nokogiri. Each node has a reference to the parent document, and to its own children. If we break the hard reference from the GC root object to the document then it and all of its nodes can be collected. The same is true if the nodes hold VALUEs and mark them, but what happens if we use handles?

Sorry, your browser does not support SVG.

Well, our diagram has certainly got messier! But it's also hard to know how we should free those objects nicely. Release the handle that Object held to Document doesn't help, because other handles exist pointing to it from the nodes, so the whole cluster remains uncollected. There doesn't seem to be a nice obvious order in which we could do it. We could solve almost any situation like this by introducing weak handles, but that requires patching each C extension, and carefully analysing how to break these cycles. To really be compatible we need a different approach.