Runtime.freeMemory() and company or early allocation to prevent late out of memory errors

Viewed 236

I am writing a program which, depending on JVM, heap size and user settings, is likely to throw some OOM errors pretty often.

I do not want to give it some arbitrary limits, but since, when this errors are going to occur, the program will likely take a lot of time to run before producing some meaningfull output, I wanted it at least to quit as soon as possible if an OOM is going to happen. My first approach was to initialize the most memory consuming objects early, which probably is not a good practice.

Since some more memory can be needed later depending on user input, I also wanted to ensure some reasonably safe margin of free memory, and, after some awful ideas, I figured out that using methods from Runtime and giving the user some data on the remaining memory, on what can be expected from that data and a warning if needed would be a more elegant solution.

One of the early initialized objects is an array used to write to an audio file, it is not used until the end of the program and I considered adding it's size to the free memory calculation and initializing it only when it is needed.

As far as I see the major flaws with calculating free memory before are the following:

  1. It is not completely accurate
  2. For some objects the actual size in heap memory cannot be predicted and depends on the implementation
  3. In corner cases the program will still terminate at the end of the computation, when the objects are initialized, not as soon as there is not enough memory left to complete the program. Doing repeated checks can be a solution, but this can affect performance.

And the major problems with early initialization are the following:

  1. It freezes some resources which aren't used, probably affecting performance.

  2. May affect readability and maintainability of the code, especially if misused and/or in larger programs.

  3. Causes the program to always exit with an error instead of preventing it and exiting in a graceful way.

What would be in most cases the best approach to this kind of problem, are there better solutions or other factors that should be considered or are some of the one I mentioned more or less important than I think?

PS I probably can and should improve memory management and this would mitigate or almost solve the problem in most cases and caring about memory management should be the priority, but I think it is still a valid question. I understand that Runtime.freeMemory() cannot be used as it seems at a first glance .

EDIT

I'll try to clarify what I mean by early OOM.

Chunk of memory A (used from the beginning, reasonably predictable by options)

Chunk of memory B (variable, depending on user input)

Chunk of memory C (used at the end, reasonably predictable by options)

Normal way: A ,B ,long computetion, C (the OOM will likely happen here)

Early initialization: A ,C ,B (the OOM will likely happen here), long computation

One important point that I missed (and probably outweights the others in most real world programs) is that some memory from A and B will likely be freed before C is used and early initialization will cause the program to fail even when there's no need for it.

2 Answers

If you want your program to quit quickly when OutOfMemoryError is thrown use -XX:+ExitOnOutOfMemoryError or -XX:+CrashOnOutOfMemoryError option. This will ensure that when OOME is thrown in your program the JVM will exit. See this article to understand the difference between these options.

Do note that OutOfMemoryError is thrown in few situations: not enough memory to allocate an object, GC overhead limit reached, file descriptor limit reached, etc.

If you want to do different things depending on the time the application goes out of memory, you can use Thread.setDefaultUncaughtExceptionHandler(...). The pre-shutdown logic will likely need some memory, which you need to allocate up-front and release when needed.

byte[] memory = new byte[4096];
final AtomicBoolean shuttingDown = new AtomicBoolean();

Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
    if (throwable instanceof OutOfMemoryError) {
        if (shuttingDown.getAndSet(true)) return; // Execute the following logic only once
        memory = null; // Release pre-allocated memory
        // Do pre-shutdown logic
        System.exit(0);
    }
});
Related