Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Ways I handled my OutOfMemoryErrors (bankstatementconverter.com)
27 points by 4pkjai on Sept 28, 2021 | hide | past | favorite | 10 comments


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.


It'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 :()


Out of memory errors are extremely dangerous in concurrent code. It is one of the few ways that will leave things in an inconsistent state as it releases the monitors and you likely aren't catching them and fixing things up. I highly recommend all Java production JVMs have one of these set:

-XX:+ExitOnOutOfMemoryError

-XX:+CrashOnOutOfMemoryError


Agree! OOM errors also leave a lot of things in weird states where they're functionally dead, but the process is still running. So depending on your monitoring everything looks 'normal'


Regarding your change to `isEqualFromIndex()`, wouldn't the results of the `lowercase()` calls be freed on successive iterations? Does this only happen on the function termination?


I think that depends on whether the VM can determine if the lower case strings escape the function at runtime. By default everything is heap allocated.


Unless I'm missing something that isEqualsFromIndex is reimplementing String's regionMatches method.

E.g. text.regionMatches(true, index, substring, 0, substring.length())


Yes you're right the functionality is the same. Thanks!


Does any one know what runtime and gc the author was using? Those are important details.


Hope this answers your question

$ java -XX:+PrintCommandLineFlags -version

-XX:G1ConcRefinementThreads=8 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=268435456 -XX:MaxHeapSize=4294967296 -XX:+PrintCommandLineFlags -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC

openjdk version "11.0.7" 2020-04-14 OpenJDK Runtime Environment (build 11.0.7+10) OpenJDK 64-Bit Server VM (build 11.0.7+10, mixed mode)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: