3 ms·
I don't like how the GC generally interact with externally-imposed memory limits, and that example with isEqualFromIndex() is a great example actually: sure, th
by Joker_vD 5y ago
I don't like how the GC generally interact with externally-imposed memory limits, and that example with isEqualFromIndex() is a great example actually: sure, the loop allocates lot of garbage but since it's garbage, nothing prevents GC from kicking in when the memory usage is close to the allowed maximum and reclaiming it, right? Except apparently in this case GC thinks there is still plenty of memory, the OS thinks otherwise, and the program gets killed.
It probably makes sense for unmanaged code to die just on OOM: it (presumably) has "accurate" memory tracking so if it runs out of memory, then it actually can't progress forward. But for managed environments OOM just means that it's time to do a stop-the-world, process-wide GC in every allocated heap.
I've seen several cases in Erlang where a long-living process would do some heavy processing with binaries (they live in a separate binary heap devoted specifically to storing binaries), send the results and then go to sleep. It leads to the situation where there would be a large amount of unneeded binaries polluting the heap that can't be collected because they are still referenced by some process; if that process did a GC, those references would go away but it didn't because those references are quite small and don't make the process's heap large enough to trigger GC. And since in Erlang GC is done only per process, not globally, there is not much one can do besides judicious sprinkling of "erlang:garbage_collect()" and "hibernate" through the codebase.
- pornel 5y agoIt's a myth with unmanaged code can run out of memory to the last byte. The probability of hitting the limit is proportional to the allocation size, so the program is most likely to hit it early on something big and easy to back out of (a buffer for a whole file, or an image bitmap) rather than literally have 0 bytes left. Additionally, allocators typically have per-size memory pools and free lists, so even when one small allocation fails, there's still a chance that other size classes will be available. In something like C error manual handling and freeing is hard, so the error code paths in unexpected places are likely to be untested and buggy. But with something like Rust the compiler writes cleanup code for you, so it can clean up precisely every time (if only Rust's libstd wasn't committing a suicide on OOM :()