You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When you have a async method on a class like the following nop method
#[derive(Debug,Clone)]#[napi]pubstructExample{pubname:String,}#[napi]implExample{#[napi(constructor)]pubfnnew(name:String) -> Self{Self{
name
}}#[napi]pubasyncfnnop(&self,result:bool) -> napi::Result<bool>{Ok(result)}}
Each call to nop will leave a leaked v8impl::Reference until the instance of Example is released. I think is caused by the issue mentioned here nodejs/node#39915 so not actually a NAPI-RS problem.
Node: v16.15.1
napi-rs: 2.16.6
The example loop that shows this is as follows
import{Example}from'../../core'asyncfunctionrun(){varcounter=0letstatus=()=>{console.log("Iterations: "+counter.toLocaleString('en-us'))letinfo=Object.entries(process.memoryUsage())for(const[key,value]ofinfo){console.log(`${key}: ${value/1000000} MB `)}process.stdout.cursorTo(0);process.stdout.moveCursor(0,-(info.length+1));}varrun=true;varoutputTime=Date.now();setTimeout(function(){run=false;},5*60000);letexample=newExample("verify")varoutputTime=Date.now();while(run){letcurrentTime=Date.now();awaitexample.nop(true)counter++if(currentTime-outputTime>5000){// If this line is uncommented the leak will not happen// example = new Example("verify")if(global.gc){global.gc();}status()outputTime=currentTime;}}}run()
The explicit GC calls are just to make it easier to see and not required.
For our use case I can create a version without the issue since we do not actually need to hold onto the reference as our struct can be cloned before going across the async boundary. This will require using less of the auto-genrated code however. Just wanted to verify that this is caused by the current napi limitations before committing to the workaround.
Is it worth changing the expansion code to use clone if the struct supports rather then using napi references for self?
The text was updated successfully, but these errors were encountered:
Looking at this a little deeper is seems that the issue is resolve in later Node versions. I think it was resolved in Node 18 when TrackedFinalizer was added to its Reference management.
Not verified that this is the cause but specifically older node leaves unlinking and deletion to finalization if the refcount is 0.
The result is in Node 16 and likely older versions is using napi on an async fn will result in increasing RSS usage for every call until the class instance is finalized.
Ideally we would be using a single Reference for every call while incrementing RC, wrapping the a node Reference to self rather then just the pointer to self would allow this but might trip up some current users who are manually unwrapping. Could also use the internal napi-rs REFERENCE_MAP to get a single instance on call but that is not ideal from a performance point of view.
The option I am drawn to is allowing the user to enable wrapping a Reference for this by adding options to the macro and documenting the limitation. Or just avoid old node versions.
raydin
changed the title
Reference Leak for async class functions until owning class instance is released
Reference Leak for async class functions until owning class instance is released on Node <= 16
May 17, 2024
When you have a async method on a class like the following
nop
methodEach call to
nop
will leave a leakedv8impl::Reference
until the instance ofExample
is released. I think is caused by the issue mentioned here nodejs/node#39915 so not actually a NAPI-RS problem.Node: v16.15.1
napi-rs: 2.16.6
The example loop that shows this is as follows
The explicit GC calls are just to make it easier to see and not required.
For our use case I can create a version without the issue since we do not actually need to hold onto the reference as our struct can be cloned before going across the async boundary. This will require using less of the auto-genrated code however. Just wanted to verify that this is caused by the current napi limitations before committing to the workaround.
Is it worth changing the expansion code to use clone if the struct supports rather then using napi references for self?
The text was updated successfully, but these errors were encountered: