Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Chrome/V8 does not garbage collect a circular reference?

Take a look at this part of a Chrome heap snapshot:

Chrome circular reference

It shows the retainers of an object in the heap that, as far as I know and can see, should be garbage, but is not collected despite that.

The "shortest" path to the root is, after all, a cyclical path (it never actually reaches the root). Which leaves one to wonder, how is the snapshot viewer even able to assign a distance of 12 to it? Is that just the number of steps it took through the cycle before giving up? Note how the distance never gets below 11.

I've read that it can take a few iterations to clean up subgraphs with circular references. But repeated forced collections (with the trash can button in the Timeline tab) failed to clean up these objects.

Note that exploring through the '185' references eventually leads to the same system / Context @862399, so there really isn't a path from the root to this object (at least not visible here).

Am I going crazy, or is the garbage collector actually broken? I don't remember having this issue in the past. I'm on Chrome 45.0.2454.101. Beta 46.0.2490.64 behaves the same.

like image 348
Bart van Heukelom Avatar asked Oct 08 '15 08:10

Bart van Heukelom


People also ask

Does V8 have garbage collection?

There are two garbage collectors in V8. The Major GC (Mark-Compact) collects garbage from the whole heap. The Minor GC (Scavenger) collects garbage in the young generation.

How does V8 garbage collection work?

In V8, the garbage collector is named Orinoco. It divides the heap memory space into 2 regions: young generation and old generation. And the young/old generation take different strategies. The minor GC for the young generation applies a much faster but space consuming algorithm called Scavenge.

Can garbage collector release isolate circular references?

The . NET garbage collector can absolutely handle circular references.

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.


1 Answers

To be completely honest we would need a quick look at some test code where you replicated this but I have a general idea of what you are experiencing. If I am wrong and you can provide some test code that proves this, please let me know.

As you seem to already know, to 'clean up', Javascript wants no more references to the item looking to be freed.

A simple example:

// Currently not eligible for garbage
var myObj = {
    data : 'test'
};

// Reference data inside myObj
// A unique identifier to myObj.data now exists 
var myData = myObj.data;

// Whoops
myObj = 'something else';

// Because myData exists, so does data and can not be freed either, understandable
// This sounds simple and logical but most do not understand what is happening in the engine
// A relationship has been born between 'myData' and 'data' and as long as it exists, it is not eligible for garbage and remains in memory
console.log( myData );

Your code is probably more complicated than this but this might help explain how, somewhere, the garbage can not be collected as the scope chain can be followed to a reference.

Consider the following

function oops(text){

    function doStuff(){
        return text.toLowerCase();
    }
    return doStuff();
}

// 'doStuff' stays alive thanks to 'test' below.
var test = oops('closure');

the function doStuff will not be garbage collected because it is being referenced by test.

// You can see where this is headed. We have 2 references to the same 'doStuff' function object with separate unique identifiers now below.
var test2 = oops('closures...');

// This is now another unique identifier to 'doStuff'
var test3 = test2;

// doStuff survives yet still
test = 0;
test2 = 0;

// Now we let the function object of 'doStuff' be freed because there is no longer any references to it
test3 = 0;

This is essentially a memory leak we created. Each time you call oops you are creating a function object doStuff with a unique identifier.

A way to avoid this could be

function doStuff( text ){
    return text.toLowerCase();
}

function oops( text ){
    return doStuff();
}

var test = oops( 'closure' );

Now we have no memory leaks. doStuff is being invoked and not created.

A closer look at your code and you will find you are probably doing this somewhere.

If you are messing with elements and I think you might be, IBM has a good article about circular references you may want to take a look at.


It's late and some of this was untested, but the theory is still there so if I misspelled something etc, let me know and I can take a look tomorrow for future visitors to this page.

like image 122
Jesse Avatar answered Sep 22 '22 03:09

Jesse