Reputation: 1
const { createCanvas } = require('canvas');
function renderImage() {
const bgCanvas = createCanvas(800, 600);
let bgCtx = bgCanvas.getContext('2d');
bgCtx = null;
global.gc();
}
console.log('Memory usage before:', process.memoryUsage());
renderImage()
console.log('Memory usage after:', process.memoryUsage());
The above code is a minimal code sample from a possible memory leak in my discord bot. It seems to be using anywhere between 20-40mb of memory with the .getContext() method and not giving it back. Any insight would be greatly appreciated since I am completely lost here.
I am using canvas ^2.11.2
This is the memory log:
Memory usage before: {
rss: 35856384,
heapTotal: 4665344,
heapUsed: 4369736,
external: 1323854,
arrayBuffers: 10515
}
Memory usage after: {
rss: 63238144,
heapTotal: 5976064,
heapUsed: 3500384,
external: 1455306,
arrayBuffers: 10475
}
Upvotes: 0
Views: 220
Reputation: 41
UPD: Temporary solution: you can set your process manager (pm2 or whatever you are using) to automatically restart when a certain memory consumption is reached.
So, I've done some research and some tests and hope this information helps you:
First, you are using the library correctly - object references are not saved and therefore they will be collected by the garbage collector.
Now, about the Canvas
library:
The getContext
call adds a context to the parent Canvas
object BUT this only happens on the first call to the function when the context object has not been created - subsequent calls to getContext
use an already created object and are therefore not memory costly. I attach the function code from the source code of the canvas
library:
Canvas.prototype.getContext = function (contextType, contextAttributes) {
if (contextType == '2d') {
const ctx = this._context2d || (this._context2d = new Context2d(this, contextAttributes))
this.context = ctx
ctx.canvas = this
return ctx
}
}
Most of the code of the 'Canvas' library is written in c++ where there is no garbage collector and memory management is done manually, so it is possible that a minor leak is hidden there. If memory leaks continue after the application is running (which I didn't get in my tests) - it makes sense to create an issue on the canvas github repository.
About GC(Garbage Collector)
To summarize - V8 Garbage Collector is a complex mechanism and it doesn't release memory immediately after removing object references, so:
global.gc()
, it works well in automatic modeRun tests on a running application that is actively running and not in “idle” mode
My tests result(Tests code)
Application started, RSS usage: 44.19584mbs
... (Canvas objects created)
RSS usage: 108.650496mbs, time since start: 2.5 minutes
... (Released a part of memory)
RSS usage: 79.773696mbs, time since start: 2.6 minutes
... (Released a part of memory)
RSS usage: 55.11168mbs, time since start: 32.3 minutes
... (Back to the original memory value)
RSS usage: 46.116864mbs, time since start: 63.4 minutes
Run code with --trace-gc
flag, like node --trace-gc index.js
for tracking GC operations
Upvotes: 0