Open jhuckaby opened 2 years ago
I haven't looked into this yet but it kind of sounds like the pango_cairo_font_map_set_default
call here might not be releasing memory, even though it's documented to do so. Does the a similar leak happen if you don't call deregisterAllFonts
?
@chearon Yup, it sure does:
MEM BEFORE: {
rss: 38981632, // 37.1 MB
heapTotal: 5742592,
heapUsed: 3029136,
external: 934283,
arrayBuffers: 25770
}
MEM AFTER: {
rss: 343392256, // 327.4 MB
heapTotal: 5353472,
heapUsed: 2714592,
external: 1581833,
arrayBuffers: 612270
}
This is with the call to deregisterAllFonts()
commented out. How interesting!
Indeed. That's a bit more evidence for a PangoFontMap leak but I can't find anything wrong with our usage... if removing the pango_cairo_font_map_set_default(NULL);
calls fixes the memory leak, we would know it's the font map.
If you run with node --expose-gc
and call global.gc()
at the end of the loop, is there still a leak? There is one PangoCairoFontMap per ctx
above, so that loop would have lots of memory until the old ctx
es are freed.
Yup, it still leaks with GC on every loop:
[jhuckaby@mendo pango-font-bug]$ node --expose-gc leak.js
MEM BEFORE: {
rss: 40337408, // 38.4 MB
heapTotal: 5742592,
heapUsed: 2982232,
external: 934304,
arrayBuffers: 25770
}
MEM AFTER: {
rss: 290988032, // 277.5 MB
heapTotal: 4829184,
heapUsed: 2529136,
external: 987162,
arrayBuffers: 17578
}
This is a bit less than usual (277 MB, down from 327 MB), so I doubled the iterations to 5,000 to see if the leak was linear, and continually getting worse as it went:
[jhuckaby@mendo pango-font-bug]$ node --expose-gc leak.js
MEM BEFORE: {
rss: 40386560, // 38.5 MB
heapTotal: 5480448,
heapUsed: 2986208,
external: 952830,
arrayBuffers: 44296
}
MEM AFTER: {
rss: 511475712, // 487.7 MB
heapTotal: 4829184,
heapUsed: 2531216,
external: 987162,
arrayBuffers: 17578
}
Yeah, it is.
What's the progress on this? We're (indirectly) using canvas to turn PDFs into pngs as an internal microservice on AWS but the containers eventually use all their memory up and need to be killed and restarted.
any updates on this one? we're using node-canvas in a serverless function that registers multiple fonts in each invocation which eventually reaches a memory limit if the function was up enough time
Any updates on this? We need it too.
There appears to be a rather severe memory leak when rendering text repeatedly, and calling
deregisterAllFonts()
andregisterFont()
between each iteration.Steps to Reproduce
Here is a simple script to reproduce and show the leak:
After running 2,500 iterations in a single process, the
rss
memory is upwards of 400MB on Linux, and 4GB on macOS.I do realize that rendering text and producing PNG images takes some memory, but this seems to continually leak the longer it runs.
I have grabbed multiple JS heap dumps and compared them in Chrome Dev Tools. The memory is definitely not in Node.js land, so it must be on the C++ side of things.
Your Environment
registerFont()
andderegisterAllFonts()
without creating and drawing into a canvas.Here is a ZIP file containing the script to reproduce, and the FuturaStdMedium OTF font:
https://pixlcore.com/public/1b019a4f65ab0694/node-canvas-font-mem-leak.zip
I suspect the problem is inside the
deregisterAllFonts()
function, which I myself wrote and introduced in PR #1811. However, I have been over the code many times and I can't see where anything could be leaking.Another note of interest: The leak on macOS is easily 10 times worse than Linux, with the final memory clocking in at 4 GB after 2,500 iterations. It is also much slower, taking almost 10 minutes to run on my 2021 MBP. My theory here is that every time you call
deregisterAllFonts()
and thenregisterFont()
, it re-registers all system fonts, and on macOS the list of built-in system fonts is vast. On my headless Linux box there are very few (if any) built-in system fonts, so the leak is slower there.Final note: You don't actually have to call
fillText()
to reproduce the memory leak. Simply creating a canvas along withregisterFont()
andderegisterAllFonts()
is enough to do it.