Version
main
Platform
Subsystem
ffi
What steps will reproduce the bug?
repro.c
#include<stdint.h>int32_tadd2(int32_ta, int32_tb) {
returna+b;
}repro.js
import{DynamicLibrary,suffix}from'node:ffi';import{stat}from'node:fs/promises';constlibPath=newURL(`./repro.${suffix}`,import.meta.url).pathname;awaitstat(libPath);constlib=newDynamicLibrary(libPath);lib.getFunction('add2',{arguments: ['i32','i32'],return: 'i32'});console.log('same wrapper on two reads:',lib.functions.add2===lib.functions.add2);// Idiomatic call through the accessor, which re-resolves on every iteration.constbefore=process.memoryUsage.rss();letsum=0;for(leti=0;i<20000;i++)sum+=lib.functions.add2(20,22);constafter=process.memoryUsage.rss();console.log(`sum: ${sum}`);console.log(`rss growth: ${((after-before)/1024/1024).toFixed(1)} MiB`);lib.close();Commands to run:
$ cc -shared -fPIC -O2 -o repro.so repro.c
$ node --experimental-ffi repro.js
How often does it reproduce? Is there a required condition?
Always
What is the expected behavior? Why is that the expected behavior?
Reading an already-resolved function off library.functions returns the same wrapper each time, and calling through the accessor in a loop does not grow memory.
What do you see instead?
same wrapper on two reads: falsesum: 840000rss growth: 56.0 MiB
Each read builds a new wrapper, so lib.functions.add2 !== lib.functions.add2, and the loop allocates a fresh trampoline page plus a new FFIFunctionInfo per iteration, holding all of them until GC.
Additional information
No response
Version
main
Platform
Subsystem
ffi
What steps will reproduce the bug?
repro.crepro.jsCommands to run:
How often does it reproduce? Is there a required condition?
Always
What is the expected behavior? Why is that the expected behavior?
Reading an already-resolved function off
library.functionsreturns the same wrapper each time, and calling through the accessor in a loop does not grow memory.What do you see instead?
Each read builds a new wrapper, so
lib.functions.add2 !== lib.functions.add2, and the loop allocates a fresh trampoline page plus a newFFIFunctionInfoper iteration, holding all of them until GC.Additional information
No response