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
Been troubleshooting a leak in https://github.com/asilvas/node-image-steam (processes millions of images every day) and originally thought it was in my project but after a number of heap dump checks I determined it wasn't a leak in V8.
In order to break it down the simplest parts I recorded the traffic in a serial form so it can be replayed in a pure sharp script.
It's downloading these files on the fly, which avoids any FS caching which will bloat memory usage, and forwards the instructions (in sharp.log) directly to sharp, one at a time.
Memory usage gets into 500MB+ within a few mins (at least on Docker+CentOS), and seems to eventually peak. On some systems I've seen over 2GB usage. Only processing a single image at a time should be pretty flat in memory usage. Have you seen this before? Any ideas? I wasn't aware of anything sharp/vips was doing that should be triggering Linux's file caching.
Edit: While memory usage on Mac is still higher than I expect for a single image processed at a time (~160MB) after a couple hundred images, it's nowhere near as high as on Linux.. And it seems to peak quickly. So it appears to be a linux only issue. Docker is also involved, so not ruling that out either.
Hello, how is memory usage being measured? If RSS, please remember this includes free memory that has not (yet) been returned to the OS, which explains why different OSs report different RSS for the same task.
Yes, RSS being the main indicator. But I also rely on buff/cache and avail memory to better understand "free-able" memory, and this indicates that this memory is never being released back. This memory doesn't seem to reside in the V8 memory space, as indicated from dozens of heap dump tests.
Thanks for links to the other issues that seem connected. I'm not entirely sure the issue is fully understood though. I'm not convinced it's an issue with sharp either, but I'm hoping we can work around the problem as the impact is quite significant. We're using ~5x the memory we should be, which becomes a big deal when you're serving (many) millions of requests/day.
I'll continue investigating the related cases. So far I've found no workaround to the problem that doesn't involve not using toBuffer.
It's worth subscribing to nodejs/node#1671 for V8 updates that will improve GC of Buffer objects.
If you're not already doing so, you might want to experiment with a different memory allocator such as jemalloc. You'll probably see less fragmentation, but that's still dealing with the effect rather than the cause.
Does the prod environment use 8 real CPU cores or is this "vCPU" hyper-threading? If the latter, perhaps also experiment halving concurrency to improve throughput (and reduce the memory effects).
Feel free to close this as a duplicate of others. But from everything I've learned of the problem thus far there doesn't seem to be any conclusive evidence that this is a Node and/or V8 issue. From the symptoms of the isolated tests I've run (as well as others) it does seem to be any issue with sharp or vips as this isn't a common problem in the node community to see run-away memory increases of this nature. I was able to verify this is not a case of GC'd memory not being released back to the OS, confirming that this memory was in-use and any reduction in available memory resulting in memory allocation failure. But as I said, nothing conclusive either way.
I tried to investigate ways to resolve/workaround the problem within Sharp but was unsuccessful -- hopefully someone with more expertise with V8 native modules will have better luck.
I haven't proven/disproven that theory. But with the modest number of objects being processed to achieve such high memory usage, it'd seem to require some pretty severe fragmentation to justify this.
Is your thoughts that it's fragmentation in V8 or native space?
I've found that running my sharp modules in a child_process spawn that exits once it's completed works really well for me. It keeps the memory load down.
Reacted by Aaron Silvas, Dragos Sebestin, agonzalez8, Vlad Voloshyn and 流浪大法师
Sharp is very fast but there's an obvious memory problem. As I send items to be processed to a worker, the memory never gets released at end of processing.
On a C5.x4large, I spawn 30 child processes. CPU ranges from 10%-30%. Memory however keeps growing until I run out of 32GB of ram in about 2 minutes.
I'll investigate and see if I can come back with a solution.
@egekhter I can confirm that using jemalloc as allocator solves the problem.
Thanks, it does seem to help a bit, but the problem is not solved on my end. I installed jemalloc and set the "LD_PRELOAD" environment variable and it kept memory usage lower for longer, however it still keeps climbing. If I stop sending new items for processing to my workers, the memory usage of the entire server does not decrease.
I originally switched from Jimp to Sharp as CPU usage was too high.
I noticed CPU usage went down but memory usage went up.
I switched from Sharp to GM.
With the only change being switching out the modules and changing the function calls (i.e from extract to crop), my used memory decreased from 32 GB to a stable 3GB.
@egekhter Whilst Node.js Worker Threads will probably help the single-threaded world of jimp, they won't offer much to help the multi-threaded world of sharp/libvips and can cause greater heap fragmentation faster.
30x worker threads each spawning 4x libuv threads each spawning 16x (c5.4xlarge vCPU) libvips threads is a concurrency of 1920 threads all allocating/freeing memory from the same pool.
If you'd like to "manage" concurrency via Worker Threads, then try setting sharp.concurrency to 1 so libvips doesn't also try to do so.
@egekhter Whilst Node.js Worker Threads will probably help the single-threaded world of jimp, they won't offer much to help the multi-threaded world of sharp/libvips and can cause greater heap fragmentation faster.
30x worker threads each spawning 4x libuv threads each spawning 16x (c5.4xlarge vCPU) libvips threads is a concurrency of 1920 threads all allocating/freeing memory from the same pool.
If you'd like to "manage" concurrency via Worker Threads, then try setting sharp.concurrency to 1 so libvips doesn't also try to do so.
Solved my problem with your help.
sharp.concurrency(1);
Up to 70x child processes and still have plenty of memory left.
Activity
lovell commentedon Sep 21, 2017
Hello, how is memory usage being measured? If RSS, please remember this includes free memory that has not (yet) been returned to the OS, which explains why different OSs report different RSS for the same task.
If you've not seen then, there have been quite a few related questions previously:
https://github.com/lovell/sharp/search?utf8=%E2%9C%93&q=rss+%22returned+to+the+OS%22&type=Issues
asilvas commentedon Sep 21, 2017
Yes, RSS being the main indicator. But I also rely on buff/cache and avail memory to better understand "free-able" memory, and this indicates that this memory is never being released back. This memory doesn't seem to reside in the V8 memory space, as indicated from dozens of heap dump tests.
Thanks for links to the other issues that seem connected. I'm not entirely sure the issue is fully understood though. I'm not convinced it's an issue with sharp either, but I'm hoping we can work around the problem as the impact is quite significant. We're using ~5x the memory we should be, which becomes a big deal when you're serving (many) millions of requests/day.
I'll continue investigating the related cases. So far I've found no workaround to the problem that doesn't involve not using
toBuffer.lovell commentedon Sep 21, 2017
It's worth subscribing to nodejs/node#1671 for V8 updates that will improve GC of Buffer objects.
If you're not already doing so, you might want to experiment with a different memory allocator such as jemalloc. You'll probably see less fragmentation, but that's still dealing with the effect rather than the cause.
asilvas commentedon Sep 21, 2017
Will do, thanks.
Probably no surprise, but I was at least able to correlate usage with the
concurrencysetting.In my isolated (sharp-only) test these were the findings:
lovell commentedon Sep 22, 2017
Does the prod environment use 8 real CPU cores or is this "vCPU" hyper-threading? If the latter, perhaps also experiment halving concurrency to improve throughput (and reduce the memory effects).
asilvas commentedon Sep 22, 2017
I am artificially limiting cores to keep memory in check, but at the cost of up to 30% slower response times. Temporary test.
asilvas commentedon Sep 25, 2017
Feel free to close this as a duplicate of others. But from everything I've learned of the problem thus far there doesn't seem to be any conclusive evidence that this is a Node and/or V8 issue. From the symptoms of the isolated tests I've run (as well as others) it does seem to be any issue with sharp or vips as this isn't a common problem in the node community to see run-away memory increases of this nature. I was able to verify this is not a case of GC'd memory not being released back to the OS, confirming that this memory was in-use and any reduction in available memory resulting in memory allocation failure. But as I said, nothing conclusive either way.
I tried to investigate ways to resolve/workaround the problem within Sharp but was unsuccessful -- hopefully someone with more expertise with V8 native modules will have better luck.
lovell commentedon Sep 25, 2017
Could memory fragmentation explain this?
asilvas commentedon Sep 25, 2017
I haven't proven/disproven that theory. But with the modest number of objects being processed to achieve such high memory usage, it'd seem to require some pretty severe fragmentation to justify this.
Is your thoughts that it's fragmentation in V8 or native space?
lovell commentedon Sep 26, 2017
Are you using Node 8? If not, do you see the same RSS levels with it?
Were you able to try jemalloc? It provides useful debugging via malloc_stats_print.
Given you're using CentOS, have you tried disabling transparent huge pages?
asilvas commentedon Sep 26, 2017
Not using 8 in prod, but yes was able to reproduce similar RSS levels (with 8.5.0) in the isolated test.
Might be a bit before I can look into the other options but will keep in mind, thanks.
lovell commentedon Oct 15, 2017
If sharpen or blur operations are being used then the small leak fixed in https://github.com/jcupitt/libvips/issues/771 may be related here.
asilvas commentedon Oct 15, 2017
The test sample for this topic doesn't use those two operations so probably unrelated. But we do use them on occasion, thanks!
trev-dev commentedon Nov 23, 2017
I've found that running my sharp modules in a child_process spawn that exits once it's completed works really well for me. It keeps the memory load down.
38 remaining items
cablespaghetti commentedon Oct 24, 2019
I switched from Debian to Alpine (and admittedly Node 11 to 12):

Thanks to everyone who suggested this as a fix!
eugenelight16 commentedon Jul 23, 2020
Sharp is very fast but there's an obvious memory problem. As I send items to be processed to a worker, the memory never gets released at end of processing.
On a C5.x4large, I spawn 30 child processes. CPU ranges from 10%-30%. Memory however keeps growing until I run out of 32GB of ram in about 2 minutes.
I'll investigate and see if I can come back with a solution.
vinerz commentedon Jul 23, 2020
@egekhter I can confirm that using
jemallocas allocator solves the problem.eugenelight16 commentedon Jul 23, 2020
Thanks, it does seem to help a bit, but the problem is not solved on my end. I installed jemalloc and set the "LD_PRELOAD" environment variable and it kept memory usage lower for longer, however it still keeps climbing. If I stop sending new items for processing to my workers, the memory usage of the entire server does not decrease.
eugenelight16 commentedon Jul 23, 2020
Another update.
I originally switched from Jimp to Sharp as CPU usage was too high.
I noticed CPU usage went down but memory usage went up.
I switched from Sharp to GM.
With the only change being switching out the modules and changing the function calls (i.e from extract to crop), my used memory decreased from 32 GB to a stable 3GB.
There's not much we can do as a user from here.
lovell commentedon Jul 23, 2020
@egekhter Whilst Node.js Worker Threads will probably help the single-threaded world of jimp, they won't offer much to help the multi-threaded world of sharp/libvips and can cause greater heap fragmentation faster.
30x worker threads each spawning 4x libuv threads each spawning 16x (c5.4xlarge vCPU) libvips threads is a concurrency of 1920 threads all allocating/freeing memory from the same pool.
If you'd like to "manage" concurrency via Worker Threads, then try setting sharp.concurrency to 1 so libvips doesn't also try to do so.
eugenelight16 commentedon Jul 23, 2020
Solved my problem with your help.
sharp.concurrency(1);Up to 70x child processes and still have plenty of memory left.
Thanks for all your work!
lovell commentedon Mar 10, 2021
Please see #2607 for a change in the default concurrency for glibc-based Linux users that will be in v0.28.0.
FoxxMD commentedon May 5, 2022
not related...but @vinerz what application were you using to monitor memory in this comment? Is that heroku's dashboard?
vinerz commentedon May 5, 2022
Hey @FoxxMD, that's New Relic's application monitor for node 😄
alcidesbsilvaneto commentedon Feb 18, 2023
Still the solution for me in 2023.
Limited sharp concurrency to reduce memory fragmentation
nandi95 commentedon Sep 3, 2023
Went from:


to:
It's a definite improvement.
rambo-panda commentedon Dec 28, 2023
use

libjemalloc.socache:true concurrency : 1use

libjemalloc.socache:false concurrency : 1use

libjemalloc.socache:true concurrency : 16use

libjemalloc.socache:false concurrency : 16use

glibccache: false concurrency:1use

glibccache:true concurrency:1use

glibccache:false concurrency : 16use

glibccache: true concurrency: 16Limited sharp concurrency to reduce memory fragmentation