Skip to content

Creating a lot of worker threads at once doesn't fully release their memory once they're terminated #51868

Closed
@aamiaa

Description

@aamiaa

Version

v20.11.1

Platform

Linux ovps2 5.15.0-1051-oracle #57-Ubuntu SMP Wed Jan 24 18:31:24 UTC 2024 aarch64 aarch64 aarch64 GNU/Linux

Subsystem

No response

What steps will reproduce the bug?

The following code creates 50 worker threads which load 20mb of data into memory and then terminates them. This is repeated 4 times, and the RSS memory usage either grows or slightly decreases with each iteration.

Main file:

const { Worker } = require("worker_threads")
const path = require("path")

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))

async function doLeak() {
	const jobs = []

	for(let i=0;i<50;i++) {
		const worker = new Worker(path.join(__dirname, "worker.js"))
		jobs.push(new Promise(resolve => {
			worker.once("message", () => {
				worker.terminate()
				resolve()
			})
		}))
	}

	await Promise.all(jobs)
}

async function main() {
	console.log("RSS on start:", Math.floor(process.memoryUsage().rss/1024/1024), "MB")
	
	for(let i=1;i<=4;i++) {
		console.log("Starting...", i)

		await doLeak()
		await sleep(5000) // This is not required, but helps show that the memory isn't released even after waiting

		console.log("Finished! RSS:", Math.floor(process.memoryUsage().rss/1024/1024), "MB")
	}
}

main()

worker.js:

const { parentPort } = require("worker_threads")
const fs = require("fs")
const path = require("path")

// Create some data in memory
const data = fs.readFileSync(path.join(__dirname, "20mb.txt"))

parentPort.postMessage("hi")

For convenience, text file with 20mb of random data: 20mb.txt

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

Every time

What is the expected behavior? Why is that the expected behavior?

RSS memory doesn't leak

What do you see instead?

RSS memory grows drastically (from ~36mb up to ~700mb after running the provided example code)

ubuntu@ovps2:~/memleak$ node index.js
RSS on start: 36 MB
Starting... 1
Finished! RSS: 469 MB
Starting... 2
Finished! RSS: 576 MB
Starting... 3
Finished! RSS: 676 MB
Starting... 4
Finished! RSS: 617 MB
ubuntu@ovps2:~/memleak$

Additional information

The memory leak only occurs if the worker threads are created at the same time (i.e. awaiting for 100ms between each worker's creation will not cause it to happen).

It also requires the worker threads to have some data in their memory, which leads me to believe the excessive RSS memory is a result of failed de-allocation of the worker threads' memory segments (I could be wrong).

Note that it doesn't seem to matter when the worker threads are terminated. Only their creation matters.

I've reproduced this on

  • Linux ovps2 5.15.0-1051-oracle #57-Ubuntu SMP Wed Jan 24 18:31:24 UTC 2024 aarch64 aarch64 aarch64 GNU/Linux
  • Linux kvps 5.4.0-62-generic #70-Ubuntu SMP Tue Jan 12 12:45:47 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

I've been unable to reproduce this on Microsoft Windows NT 10.0.19045.0 x64

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions