majidarif
majidarif

Reputation: 19975

node.js v8 garbage collection doesn't seem to trigger

I am confused as my application is leaking memory. It is a tcp server that process hundreds of thousands of packets per minute. I have checked the code, improved it and profiled the memory.

Everything seems okay, testing locally with low traffic actually shows that the gc releases the memory properly. But when on the live heavy traffic server it doesn't.

So I tried using the expose-gc option and added the forced gc to every disconnection and now I found that the memory is not leaking anymore or was it every even leaking?

So, my conclusion is the gc didn't activate. My server has 3GB of memory and the application in just a few hours gets to eat 2.8GB of that.

Now with the forced gc the application is not leaking anymore. It is maintaining around 200MB of memory.

So, my question, why wasn't the gc getting triggered?

Upvotes: 8

Views: 3652

Answers (2)

user5612733
user5612733

Reputation:

I am noticing the same problem with garbage collection not happening. Testing my server dishing up the same content (Rendered by react) repeatedly to a test script memory would be exhausted in an hour or so. This AWS instance only has 1GB RAM.

After forcing garbage collection whenever the heapUsed exceeded 256MB everything works fine for hours on end. heapUsed oscillates from about 150MB after a GC to a little over 256MB.

Meanwhile heapTotal eventually stabilizes at about 340MB.

My conclusion is that my server does not leak memory, garbage collection is not happening as expected.

Here is what I do:

setInterval (function() {
    let mu = process.memoryUsage();
    console.log('heapTotal:',  mu.heapTotal, 'heapUsed:', mu.heapUsed);
    if (mu.heapUsed > 256 * 1024 * 1024) {
        console.log('Taking out the garbage');
        global.gc();
    }
}, 1000 * 60);

It would perhaps be better to check memory after every request.

Upvotes: 1

Selfish
Selfish

Reputation: 6190

Source: StrongLoop Blog

The fundamental problem garbage collection solves is to identify dead memory regions (unreachable objects/garbage) which are not reachable through some chain of pointers from an object which is live. Once identified, these regions can be re-used for new allocations or released back to the operating system

The leaking of memory when using event listeners is quite common and is caused by a situation where the object that is listening is prevented from being garbage collected because the event emitter, also an object, is keeping a reference to it.

So in your code onSuccess method will be referenced by your request object. However, that onSuccess is only one function that is being reused as a listener for all request objects so that should not lead to memory buildup.

In order to find the real cause of objects being left alive in your code, I'd check around the connection ending and make sure no pointers are left alive. In addition, not in some cases V8 will create an instance of a function for every time it is used, and this may be the case in hand. If both are combined, your allocated memory will keep piling instances of the callback.

Objects which have survived two minor garbage collections are promoted to “old-space.” Old-space is garbage collected in full GC (major garbage collection cycle), which is much less frequent. I assume this is far fetched, but the option of you allocating faster than sweeping cycles might be considered, and thus when you trigger garbage collection manually, a full GC cycle is triggered.

To ensure fast object allocation, short garbage collection pauses, and the “no memory fragmentation V8” employs a stop-the-world, generational, accurate, garbage collector. V8 essentially stops program execution when performing a full garbage collection cycle.

This can explain why the memory does not leak when V8 is forced into garbage cleaning.

Upvotes: 3

Related Questions