Automattic / Automattic/node-canvas
Severe memory leak when rendering text repeatedly and using `deregisterAllFonts()`
- Dominant language
- JavaScript
- Stars
- 10.7k
- Forks
- 1.2k
- Avg merge
- 4d 8h
- Merged PRs (30d)
- 1
Description
There appears to be a rather severe memory leak when rendering text repeatedly, and calling `deregisterAllFonts()` and `registerFont()` between each iteration.
## Steps to Reproduce
Here is a simple script to reproduce and show the leak:
```js
// This test demonstrates a memory leak when repeatedly registering, rendering, and deregistering a font.
// Run the script and look at the memory output, specifically the "rss" (resident memory) reading.
const Canvas = require('canvas');
console.log( "MEM BEFORE: ", process.memoryUsage() );
for (var idx = 0; idx < 2500; idx++) {
Canvas.registerFont('futurastd-m.otf', { family: 'FuturaStdMedium' });
var canvas = Canvas.createCanvas(640, 100);
var ctx = canvas.getContext('2d');
var text = 'Now is the time for all good men.';
ctx.font = 'normal normal 36px FuturaStdMedium';
ctx.fillText(text, 10, 35);
var buf = canvas.toBuffer();
buf = null;
ctx = null;
canvas = null;
Canvas.deregisterAllFonts();
}
console.log( "MEM AFTER: ", process.memoryUsage() );
```
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
- Reproducible on both Linux (CentOS 7.7) and macOS (Monterey 12.1), using Node.js 16.13.0.
- Reproducible in both node-canvas v2.9.0 with precompiled binaries, and latest HEAD revision using source compile.
- Reproducible using both OTF and TTF fonts.
- Interestingly **not** reproducible if the script simply calls `registerFont()` and `deregisterAllFonts()` 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](https://github.com/Automattic/node-canvas/pull/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 then `registerFont()`, 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 with `registerFont()` and `deregisterAllFonts()` is enough to do it.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.