开发者

Behaviour of JVM during out of memory error? List s = new ArrayList<String>();

开发者 https://www.devze.com 2023-04-12 14:09 出处:网络
try { for(;;) { s.add(\"Pradeep\"); } } finally { System.out.println(\"In 开发者_如何学运维Finally\");
try {
    for(;;) {
        s.add("Pradeep");
    }
} finally {
    System.out.println("In 开发者_如何学运维Finally");
}

In the try block the jvm runs out of memory,then how is jvm excuting finally block when it has no memory?

Output:

In Finally
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space


Presumably the System.out.println call requires less memory than the s.add("Pradeep") call.

If s is an ArrayList for instance, the s.add call may cause the list to attempt to double up it's capacity. This is possibly a quite memory demanding operation, thus it is not very surprising that the JVM can continue executing even if it can't perform such relatively expensive task.


Here is more simple code that demonstrated better what happens:

    try {
        int[] a = new int[Integer.MAX_VALUE];
    } catch( Exception e ) {
        e.printStackTrace();
    }

The allocation of the array fails but that doesn't mean Java has no free memory anymore. If you add items to a list, the list grows in jumps. At one time, the list will need more than half of the memory of the VM (about 64MB by default). The next add will try to allocate an array that is too big.

But that means the VM still has about 30MB unused heap left for other tasks.

If you want to get the VM into trouble, use a LinkedList because it grows linearly. When the last allocation fails, there will be only very little memory to handle the exception:

    LinkedList<Integer> list = new LinkedList<Integer>();
    try {
        for(;;) {
            list.add(0); 
        }
    } catch( Exception e ) {
        e.printStackTrace();
    }

That program takes longer to terminate but it still terminates with an error. Maybe Java sets aside part of the heap for error handling or error handling doesn't need to allocate memory (allocation happens before the code is executed).


In the try block the jvm runs out of memory, then how is jvm excuting finally block when it has no memory?

The JVM "runs out of memory" and throws an OOME when an attempt to allocate an object or array fails because there is not enough space available for that object. That doesn't mean that everything has to stop instantly:

  • The JVM can happily keep doing things that don't entail creating any new objects. I'm pretty sure that this is what happens in this case. (The String literal already exists, and implementation of the println method is most likely copying characters into a Buffer that was previously allocated.)

  • The JVM could potentially allocate objects that are smaller than the one that triggered the OOME.

  • The propagation of the OOME may cause variables containing object references to go out of scope, and the objects that they refer to may then become unreachable. A subsequent new may then trigger the GC that reclaims said objects, making space for new ones.


Note: the JLS does not specify that the String object that represents that literal must be created when the class is loaded. However, it certainly says that it may be created at class load time ... and that is what happens in any decent JVM.


Another answer said this:

Maybe Java sets aside part of the heap for error handling or error handling doesn't need to allocate memory (allocation happens before the code is executed).

I think this is right. However, I think that this special heap region is only used while instantiating the OOME exception, and filling in the stack trace information. Once that has happened, the JVM goes back to using the regular heap. (It would be easy to get some evidence for this. Just retry the add call in the finally block. If it succeeds, that is evidence that something has made more memory available for general use.)


The JVM isn't really out of memory and unable to proceed. This error says that it failed to allocate memory and so it didn't. That might mean its very low. But here what failed was resizing the collection's internal array which is huge. There's a lot of memory left just not that much to double a large array. So it can proceed just fine with finally.


The error is thrown when the heap space exceeds that set by the -Xmx flag, and it cannot continue as normal. The error propagates, but it does not immediately cause the JVM to be shutdown (of the system exited in such cases, there would be no point in the OOM error, as it could never be thrown).

As the JVM has not exited it will try to, as according to the language spec, execute the finally block.


Finally executes almost always. When the exception was thrown, the JVM collected as much as memory as possible, which, reading your code, probably meant that it collected the whole s collection.

When the finally is reached, it only has to create a new string "In finally" in the string pool no additional memory is required, and it has no problems since it has freed up space before.

Try printing s.size() on the finally, you'll see how it is not able to do it. (EDIT: if in catch, finally or after the try block, there´s a line of code using the s collection, the Garbage Collector is unable to collect it at the moment the OOME is thrown. This is why the heap memory will be almost full, so any new object allocation may throw another OOME again. It is difficult to predict without seeing the complete code).

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号