-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Memory leak #21
Comments
Are you able to share the gist, or a gist of the gist? libvips manages its own internal file and operation cache, defaulting to a soft limit of 100MB. This soft limit can be changed via sharp.cache(limit). To see if this is the source of heap (mis-)usage, try setting it to Are Buffers involved? If so, there may be heap allocated for Buffer objects but now remains in unused virtual memory that v8 has decided it might need in the future. You could try Node's Do you see the same problems with both Node 0.10 and 0.11? If things are better with 0.11 then you could have run into one of the problems targeted by nodejs/node-v0.x-archive#5720. |
I created the gist just for this issue, i forgot to copy paste it :/ (updated my post) It seems to be buffer related, if i return an empty buffer, it doesn't seem to leak. I faced it on 0.10, i will try on node master. |
Well, it's worse on node master (150MB vs 120MB) after calling global.gc() I think we need to go deeper. |
Thanks for all the extra info Pierre, I've been able to reproduce this locally. I see the process-level I'll experiment with the version of NanNewBufferHandle that takes a FreeCallback to discover if the node::Buffer created at sharp.cc:309 is being correctly freed at GC time. Failing that, valgrind is our friend. |
Some Initial findings... Even when libvips cache limit is set to zero, it always holds on to at least one operation. I'll check with John to see if this is a feature or a bug. This cache might account for up to half of the Resident Set Size (RSS) value. I've run memleak,js through valgrind and there are a few small leaks I need to take a closer look at:
The RSS value can include previously allocated/used memory yet to be returned to the OS, which might not always mean there's a leak. Perhaps try running other memory-intensive processes at the same time to see if the RSS value drops. |
I'll attempt to fix the inability to disable the operation cache via https://github.com/jcupitt/libvips/issues/119. Another thought: the RSS value also includes shared libraries that could be hogging/leaking memory. For example libvips uses Optimized Inner Loop Runtime Compiler, which was updated last week (v0.4.19) to "fix many memory leaks". |
The Valgrind test run with libvips compiled to use ORC v0.4.18:
Valgrind test run with libvips
Disabling, or better upgrading to v0.4.19 is highly recommended. |
It looks like orc-0.4.19 is freeing too much memory :-( see jcupitt/libvips@d990f5c apply that patch and live with the small leak, or disable orc |
Thanks John, are you planning to file a bug report upstream? I'm going to recommend disabling liborc for now. |
Yes, I need to test against their git master and maybe make a tiny test case. |
I've managed to make a test-case for the orc crash and filed a bug upstream: https://bugzilla.gnome.org/show_bug.cgi?id=731227 Follow that to track progress on this issue (hopefully). Perhaps this should be split out to a separate issue in sharp, this is no longer a memleak, really. |
They've fixed the bug in orc, I now see clean runs with their patch. orc-0.4.20 should include this fix. vips master now frees orc programs again, so the situation is:
|
Thank you @jcupitt. I'll wait until liborc v0.4.20 is available via the usual package manager suspects before recommending its use again. |
Found three more leaks we can fix, one covered by 0d89131 and two by https://github.com/jcupitt/libvips/pull/164. Everything else seems to be caused by Node-land code not freeing unreferenced Buffer objects during garbage collection. |
The change in commit bac367b is to notify the underlying V8 engine of libvips' cache via its This provides it with a more accurate picture of memory usage, ensuring garbage collection occurs at more relevant frequencies to prevent paging. |
@pierreinglebert are you able to re-run your tests using the latest master branch of both libvips and sharp to ensure we've mopped up the spillages? In addition, liborc v0.4.22 is now available and I can see a few recent commits to fix memory leaks. |
The unit tests can now be run via valgrind to check for memory leaks:
It uses a combination of libvips' suppressions file and its own based on my research into this issue. Node.js, at least v0.10, doesn't GC the heap before exiting hence all the lingering Buffer allocations. Thanks for reporting this @pierreinglebert. I'm going to close this task now but feel free to re-open if you're still having problems. |
I'm having much the same @pierreinglebert was, however I'm not using a libvips version compiled with orc. Running this small test script with 1M iterations (instead of the listed 1K) against a 2855x2855 jpeg will typically result in an OOM error after about 10 minutes (this is on a x86_64 Centos 6.4 VM with 4 cores and 2GB of memory). I've tried this with both libvips-7.40.8 and the latest libvips commit (compiled with the default configuration), as well as with sharp-0.6.2 and the latest sharp commit. I've run valgrind against the script, using your leak.sh as a template, and come up with the these results. |
Thanks @eschwim, I'll take a closer look. The consistency of "24,000 bytes in 1,000 blocks" involving |
libvips caches the most recent 1,000 operations by default, I wonder if that's part of it. You can use I can't remember which of the cache limiting things sharp uses at the moment. It shouldn't use 24kb per operation though, that seems much too high. |
Hi @jcupitt, thanks for helping here. The cache max defaults are 100MB and 500 ops in sharp - see https://github.com/lovell/sharp/blob/master/src/sharp.cc#L851-L852 @eschwim's script calls |
Ah OK. I wonder why it's exactly 1,000? It seems odd it's such a neat number. |
Just in case you want to repro, here's the exact valgrind call I made: curl -O https://raw.githubusercontent.com/jcupitt/libvips/master/libvips.supp
curl -O https://raw.githubusercontent.com/lovell/sharp/master/tests/sharp.supp
G_SLICE=always-malloc G_DEBUG=gc-friendly valgrind \
--suppressions=libvips.supp --suppressions=sharp.supp \
--leak-check=full --show-leak-kinds=definite,indirect,possible \
node test_sharp_leak.js I was using: With this input image |
I've added a comment about a possible source of this leak in jcupitt/libvips@862d7f0 |
There was an embarrassing double allocate, but that's only been there for 24 hours, and would only leak (I think) 20 bytes per operation. There's probably something nastier happening as well, unfortunately. |
Thanks John,
@eschwim please can you try again against libvips 7.40. |
I tried a couple of things. I ran I then modified
I tried with a 1k x 1k jpeg as well, which was a bit quicker:
Which looks like the behaviour you'd expect from memory fragmentation. |
Thanks John. Have you considered replacing occurrences of |
... I tried again with the cache enabled and it stabilised at 136012 after 2000 iterations. This is with Orc disabled. |
The standard linux malloc() already does slab allocation and has a magazine layer, so I'm not sure it would make a large difference there. And vips tries hard to not call malloc() and free() during processing, so it could only reduce setup cost. The standard Windows malloc is extremely slow and primitive (because of compatibility problems) so it could well be useful. I'll add a note to the TODO. |
Using libvips-26e9248: ==1569== LEAK SUMMARY: Yay! :) Thanks, guys! |
@lovell found it, but yes, good result! |
Thanks all, closing this problem. John: If you're not aware of it, jemalloc might be worth looking at if you ever go down the g_new replacement route, especially if Windows performance is of concern. Coincidentally, Node.js 0.10 has its own slab allocator but this has been removed in 0.11 unstable (and the future 0.12 stable). |
Although, sadly, the original problem that I was having still persists. Replacing the for loop in the original test script with a while(1) loop will still result in the process eventually being reaped by the OOMkiller. You can see the RSS balloon, below: [eric@test-box ~]$ node ./test_sharp_leak.js & [eric@test-box ~]$ dmesg | tail -2 |
Hi @eschwim, using You can monitor queue depth with Remembering that with the asynchronous power of Node.js comes great responsibility, perhaps try using async.parallelLimit(...) in your test. |
Ah yes, I definitely need to heed uncle Ben's words more often. I will give it a go; thanks! |
Well, Uncle Ben is going to be very disappointed with me. In an effort to debug my problems, I've attempted a couple of different ways of getting more synchronous behavior. But I have yet to come up with a solution that doesn't result in monotonic memory growth and eventual OOM death. Perhaps more enlightening is this httpd script (adapted from your example). Querying this with a simple synchronous shell loop ( FWIW, I've taken a few heap dumps (using node-heapdump) and run them through the the Chrome Dev Tools comparison tool, but nothing immediately jumps out at me. Just for some background, I'm not doing this strictly as an academic exercise 😀 I have a web proxy that I've written (using sharp) that works fantastically, with the one exception that it falls over after a few million requests. 😢 |
Both the JS tests still create as many queued tasks as memory will allow before exhausting it. Perhaps try something like https://gist.github.com/lovell/f79476202c6cb9533f8e With this example the RSS does slowly creep up, which could be caused by heap fragmentation.
You could try altering the
|
FYI, we had to process 650000 of 800x600 images and It certaintly worth to automate libvips build on install or write instructions to README. |
I ran a batch using sharp on ~1000 files and it ended using >300MB ram.
I created a little gist to "demonstrate" it, it download a large image and creates 800 thumbs at different sizes.
On my mbp, when finished, it takes 120MB ram (more if i increase the number of generated thumbs).
The text was updated successfully, but these errors were encountered: