oven-sh / bun

Incredibly fast JavaScript runtime, bundler, test runner, and package manager – all in one
https://bun.sh
Other
73.44k stars 2.7k forks source link

Likely memoryleak inside bun runtime on service http requests #14065

Open woehrl01 opened 1 week ago

woehrl01 commented 1 week ago

What version of Bun is running?

1.1.29+6d43b3662

What platform is your computer?

Linux 6.1.102 x86_64

What steps can reproduce the bug?

Running it continuously and serving millions of requests, then you can see a slight increase in memory over time. The heap stays the same size as it can be seen by heap dumps. But the overall memory consumption increases.

Please see attached screenshots. I highly appreciate it if you could guide me to additional things I could do to track down that (likely) issue in the runtime.

What is the expected behavior?

Having constant memory usage

What do you see instead?

Increase in memory usage over time

Additional information

Heap dump compare between a fresh container and a long running one, which has a memory difference of 700mb

Bildschirmfoto 2024-09-07 um 23 20 56

memory usage over time: Bildschirmfoto 2024-09-20 um 20 56 05

heapSize and heapCapacity stays the same (with margins):

Bildschirmfoto 2024-09-20 um 20 56 48
Jarred-Sumner commented 3 days ago

Unfortunately, without any code for us to reproduce this, it's difficult for us to take any action.

We have a large number of memory leak tests for Bun.serve(), and some for node:http (which currently builds on top of Bun.serve())

We would love to help if you could give us some code to look at

Judging from that screenshot there at the bottom, what sticks out the most is protectedObjectCount. That number should rarely ever go above 100 or so. These are strong handles referenced by native code. If you console.log getProtectedObjects in bun:jsc, it will dump what is being kept alive. Note that sometimes JSC will keep alive the source code for various functions that have not finished executing yet (such as those UnlinkedFunctionExecutable's there)

What are the different parts of Bun in use? Are you using node:zlib by chance?

woehrl01 commented 3 days ago

Thank you @Jarred-Sumner . I would like to send over a reproducable example, just don't know yet exactly what to look for.

We are not actively using node:zlib, the only thing from bun we are using is bun.serve() and await req.json().

I'll get back to you after I have observed the output from getProtectedObjects, thank you for that hint!

Jarred-Sumner commented 3 days ago

One thing you can also try is inserting Bun.gc() in some places. This forcefully runs the garbage collector.

are you using AbortSignal? The garbage collection rules for AbortSignal are interesting.

Are you using request.clone() or referencing request.body anywhere?

woehrl01 commented 3 days ago

@Jarred-Sumner We added Bun.gc() and also played around with the different env settings to limit the heap sizes, without any success. We also don't use (by intention) AbortSignal in that path, we are just generating lots of objects and serialize them, without any IO in between.

I just got the following error on serializing getProtectedObjects which looks "interesting", will keep you updated as soon as I have made a fix to deduplicate the objects on serialization:

TypeError: JSON.stringify cannot serialize cyclic structures.
TypeError: JSON.stringify cannot serialize cyclic structures.