Monday November 28, 2005 OutOfMemoryError looks a bit better! OutOfMemoryError has always been a confusing error. For a long time the HotSpot Virtual Machine threw this error without a detail message or stack trace so typically the thread throwing the error would terminate with this:
Exception in thread "main" java.lang.OutOfMemoryError
That was confusing. Is my java heap full or does it mean something else? Those familiar with the heap layout that the HotSpot VM uses will know that the "something else" might mean the "permanent generation". This is place where reflective data such as class and method objects are allocated. It is also the place where interned strings are stored. If you've got an application that loads a huge number of classes or interns millions of strings then it's possible that the OutOfMemoryError is because the permanent generation is full rather than the java heap.
In 5.0 the error is less confusing as there is a detail message. This means you will see something like this:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap spaceor:
Exception in thread "main" java.lang.OutOfMemoryError: PermGen full
In Mustang, there has been further changes to the way that OutOfMemoryError is reported. One obvious one is that the HotSpot VM will attempt to include a stack trace. This means you should see something like this:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at ConsumeHeap$BigObject.(ConsumeHeap.java:22)
at ConsumeHeap.main(ConsumeHeap.java:47)
In this example we see the stack trace where the allocation failed. If the OutOfMemoryError is because the perm gen it full then you might see String.intern or ClassLoader.defineClass near the top of the stack.
Is a stack trace useful? In some limited cases it can be. For example, suppose you've got a thread looping and in the loop it is allocating objects and putting them into a collection. In that case the stack trace might direct you to a good starting place. More generally, the stack trace is not likely to be useful. If you've got a busy application and the heap is nearly full, then OutOfMemoryError will be likely be thrown in some random place by some random mutator.
One useful improvement is the -XX:+HeapDumpOnOutOfMemoryError option which tells the HotSpot VM to generate a heap dump when an allocation from the java heap or the permanent generation cannot be satisfied. There isn't any overhead to running with this option so it should be useful for production systems where OutOfMemoryError takes weeks (or longer) to surface. The heap dump is in HPROF binary format so it can be analyzed using any tools that can import this format. If you read Sundar's blog then you'll know that Mustang includes a useful tool called jhat which can be used to do rudimentary analysis of the dump. jhat supports Object Query Language so you can easily create your own queries and mine the heap dump.
The next improvement is visible when the system is almost out of swap space and an allocation from the native heap fails in the VM. In that case the VM aborts and you get a one-line error such as the following:
Exception java.lang.OutOfMemoryError: requested 16 bytes for CHeapObj-new. Out of swap space?
This message has been known to confuse a lot of developers. At first glance it might it might look like that the java heap is full. Of course if you increase the size of the heap with the -mx option it might make the problem worse as the larger java heap means there is less native memory available.
In Mustang this condition will trigger the VM to invoke the fatal error handling mechanism. This means you should get a fatal error log as you would get with a normal (abnormal?) crash. The fatal error log is named hs_err_<pid>.log and contains useful information about the thread, process, and system at the time of the crash. In the case of native heap exhaustion then the heap memory and memory map information in the log can be useful. The exact format is version and platform specific and you will get more information in the J2SE Troubleshooting Guide.
Hopefully, developers will find these improvements useful. It should make OutOfMemoryError a little less confusing and it means the error is no longer a one-line wonder. ( Nov 28 2005, 06:05:37 AM PST ) Permalink Comments [16]
Posted by Matthias Ernst on November 28, 2005 at 10:56 AM PST #
Posted by alanb on November 28, 2005 at 12:32 PM PST #
Posted by 208.18.90.115 on November 29, 2005 at 11:58 PM PST #
Posted by Michael Skells on December 02, 2005 at 02:43 AM PST #
Indeed, it could be useful. Matthias mentioned he was using Tomcat so I don't know if he's got the opportunity to set the default uncaught exception handler. That said, his mileage will vary. If the java heap is completely exhausted then it might not be possible to do very much in the uncaughtException implementation. System.exit might be okay. The script or whatever that started the server will then need to look after restarting it.
Posted by alanb on December 02, 2005 at 04:30 AM PST #
import java.lang.reflect.*;
public class Invoker{
  public static void main(String[] args){
   int count = 5;
   for(int i=0; i < count; i++){
   try {
    Class.forName("MemoryConsumer").getMethod("main", new Class[] {String[].class}).invoke(null, new Object[] {args});
   } catch (InvocationTargetException ite) {
    System.err.println("THROWABLE: "+ite+ " " + ite.getTargetException());
   } catch (Exception e){
    System.err.println("THROWABLE: "+e);
   }
   System.gc();
   System.gc();
   System.err.println("After loop #"+(i+1)+" Free memory: "+Runtime.getRuntime().freeMemory());
   try{Thread.sleep(5000);}catch(InterruptedException ie){System.err.println(ie);}
   }
  }
}
</code> Probably not a "best practice", but it works. After the OOME is thrown, there are no references to any class/object in the "MemoryConsumer" application, so the gc is able to free a lot of memory.
Another solution would be to use a native script (batch/sh etc), but that won't be 100% java...
Any opinions?
Posted by 129.241.16.2 on December 02, 2005 at 10:17 AM PST #
Posted by 129.241.16.2 on December 02, 2005 at 10:20 AM PST #
Invoker only catches exceptions thrown by the main thread. When the java heap is full then OOME could be thrown by a different thread or by many threads at around the same time. Even if you catch the OOME then there isn't a general way to recover, and any attempting recovery could also fail with OOME.
Posted by alan on December 02, 2005 at 01:46 PM PST #
Posted by simon on January 11, 2006 at 05:55 AM PST #
Posted by 129.78.64.106 on April 12, 2006 at 12:22 AM PDT #
Posted by Carfield Yim on August 31, 2006 at 10:32 AM PDT #
Posted by guanhua on March 23, 2007 at 09:38 PM PDT #
Posted by daad on April 30, 2007 at 02:13 AM PDT #
Posted by ganesh modgekar on July 18, 2007 at 04:46 AM PDT #
You said: "There is another -XX option which can be used to execute actions when the OOME is thrown."
What is that option? I've seen rumours of a -XXOnError, but I haven't been able to find more details about how to use this option, if it exists.
I want to be able to tell the JVM to die on an OOM, instead of having it hang around in a crippled state. Then something like the Java Service Wrapper can restart it for another try.
Posted by Albert Strasheim on September 27, 2007 at 08:31 AM PDT #
Here is a good list of jvm options http://blogs.sun.com/watt/resource/jvm-options-list.html
Posted by Jack on March 04, 2008 at 01:12 PM PST #