Skip to content

Referenced Object never be recycled even if napi_delete_reference called #39915

Description

@Brooooooklyn

Version

14.17.5

Platform

All

Subsystem

No response

What steps will reproduce the bug?

See https://github.com/Brooooooklyn/node-ref-object-leak for detail

I have a js function in native side, which accept a JsBuffer:

#[js_function(1)]fnref_buffer(ctx: napi::CallContext) -> napi::Result<napi::JsNumber>{let buffer = ctx.get::<napi::JsBuffer>(0)?;let ref_buffer = buffer.into_ref()?;let ref_count = ref_buffer.unref(ctx.env.clone())?;
ctx.env.create_uint32(ref_count)}

The napi_create_reference, napi_reference_unref and napi_delete_reference are called in this function.

In the JavaScript side:

const{ readFileSync }=require('fs')const{ refBuffer }=require('./index.node')const{ displayMemoryUsageFromNode }=require('./util')constBigBuffer=readFileSync('yarn.lock')constinitial=process.memoryUsage()asyncfunctionmain(){for(const[i]ofArray.from({length: 10000000}).entries()){letrefCount=refBuffer(BigBuffer)if(refCount){thrownewError(`refCount should be 0, but got ${refCount}`)}if(i%1000000===0){awaitsleep()// Async function to give GC a chanceif(typeofglobal.gc==='function'){global.gc()}displayMemoryUsageFromNode(initial)}}}functionsleep(){returnnewPromise((resolve)=>{setTimeout(resolve,1000)})}main()

We can see the memory is growing:

╔══════════╤═══════════╤══════════╤══════════╤══════════════╗
║ rss │ heapTotal │ heapUsed │ external │ arrayBuffers ║
╟──────────┼───────────┼──────────┼──────────┼──────────────╢
║ +80.1 MB │ +80 MB │ +80 MB │ +40 B │ 0 B ║
╚══════════╧═══════════╧══════════╧══════════╧══════════════╝
╔═════════╤═══════════╤══════════╤══════════╤══════════════╗
║ rss │ heapTotal │ heapUsed │ external │ arrayBuffers ║
╟─────────┼───────────┼──────────┼──────────┼──────────────╢
║ +250 MB │ +85.3 MB │ +79 MB │ -170 kB │ -162 kB ║
╚═════════╧═══════════╧══════════╧══════════╧══════════════╝
╔═════════╤═══════════╤══════════╤══════════╤══════════════╗
║ rss │ heapTotal │ heapUsed │ external │ arrayBuffers ║
╟─────────┼───────────┼──────────┼──────────┼──────────────╢
║ +414 MB │ +85.3 MB │ +79.7 MB │ -170 kB │ -162 kB ║
╚═════════╧═══════════╧══════════╧══════════╧══════════════╝
╔═════════╤═══════════╤══════════╤══════════╤══════════════╗
║ rss │ heapTotal │ heapUsed │ external │ arrayBuffers ║
╟─────────┼───────────┼──────────┼──────────┼──────────────╢
║ +576 MB │ +85.3 MB │ +79.3 MB │ -170 kB │ -162 kB ║
╚═════════╧═══════════╧══════════╧══════════╧══════════════╝
╔═════════╤═══════════╤══════════╤══════════╤══════════════╗
║ rss │ heapTotal │ heapUsed │ external │ arrayBuffers ║
╟─────────┼───────────┼──────────┼──────────┼──────────────╢
║ +739 MB │ +85.3 MB │ +79.1 MB │ -170 kB │ -162 kB ║
╚═════════╧═══════════╧══════════╧══════════╧══════════════╝
╔═════════╤═══════════╤══════════╤══════════╤══════════════╗
║ rss │ heapTotal │ heapUsed │ external │ arrayBuffers ║
╟─────────┼───────────┼──────────┼──────────┼──────────────╢
║ +902 MB │ +85.3 MB │ +79 MB │ -170 kB │ -162 kB ║
╚═════════╧═══════════╧══════════╧══════════╧══════════════╝
╔══════════╤═══════════╤══════════╤══════════╤══════════════╗
║ rss │ heapTotal │ heapUsed │ external │ arrayBuffers ║
╟──────────┼───────────┼──────────┼──────────┼──────────────╢
║ +1.06 GB │ +85.3 MB │ +79 MB │ -170 kB │ -162 kB ║
╚══════════╧═══════════╧══════════╧══════════╧══════════════╝
╔══════════╤═══════════╤══════════╤══════════╤══════════════╗
║ rss │ heapTotal │ heapUsed │ external │ arrayBuffers ║
╟──────────┼───────────┼──────────┼──────────┼──────────────╢
║ +1.23 GB │ +85.3 MB │ +79.1 MB │ -170 kB │ -162 kB ║
╚══════════╧═══════════╧══════════╧══════════╧══════════════╝
╔══════════╤═══════════╤══════════╤══════════╤══════════════╗
║ rss │ heapTotal │ heapUsed │ external │ arrayBuffers ║
╟──────────┼───────────┼──────────┼──────────┼──────────────╢
║ +1.39 GB │ +85.3 MB │ +79 MB │ -170 kB │ -162 kB ║
╚══════════╧═══════════╧══════════╧══════════╧══════════════╝
╔══════════╤═══════════╤══════════╤══════════╤══════════════╗
║ rss │ heapTotal │ heapUsed │ external │ arrayBuffers ║
╟──────────┼───────────┼──────────┼──────────┼──────────────╢
║ +1.55 GB │ +85.3 MB │ +79 MB │ -170 kB │ -162 kB ║
╚══════════╧═══════════╧══════════╧══════════╧══════════════╝

How often does it reproduce? Is there a required condition?

Always

What is the expected behavior?

Buffer is deallocated

What do you see instead?

Buffer is never been deallocated

Additional information

Brooooooklyn/snappy#25

Metadata

Metadata

Assignees

No one assigned

    Labels

    node-apiIssues and PRs related to the Node-API.stale

    Type

    No type

    Projects

    Status
    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions