Tuesday, August 12, 2008

Garbage Collection, or an OutOfMemoryError Guide

I've had my share of struggling with the Java Hotspot GC and I'm sure I'm not alone. I see semi-regular postings about OutOfMemoryErrors (OOMEs) on the solr-user list. Many of these are as simple as someone not realizing that they need to pass a max heap parameter (-Xmx) to the jvm to run any kind of serious application, but occasionally the issues are more subtle. Here are some tips and notes that I've put together while building applications using Sun's Hotspot JVM.

  1. If you haven't already, turn on GC logging by passing the following options to the jvm: "-XX:+PrintGC -XX:+PrintGCDetails"
    • It is tremendously easier to debug GC/OOME problems if you can see what the GC is doing.
    • For each "Full GC", you'll see three sizes for each of the three generations (young, old, perm): (1) space occupied before the GC, (2) space occupied after the GC, and (3) size of the generation. If #2 is close to #3 for old or perm, you likely need to increase the maximum size for that generation.
  2. The Permanent Generation stores class information and interned strings. If you need to change the max size, use the option -XX:MaxPermSize (e.g. -XX:MaxPermSize=150m).
  3. The New and Old generations share the heap space. If you get a heap space OOME or your old gen space is getting low, you need to do one of two things: (1) increase the max heap size using -Xmx (e.g. -Xmx1g), or (2) increase the new ratio -XX:NewRatio (e.g. -XX:NewRatio=8), which determines how the old and new generations split up the heap. If you're on 32-bit, try #1; if you're on 64-bit try both.
  4. 64-bit JVM objects take up more space than 32-bit JVM objects. The size difference varies by object, but don't be surprised if you need 50% more heap when moving from a 32-bit JVM to a 64-bit one. Some size comparisons.
The need to tune NewRatio was a recent discovery for us and seems to be somewhat specific to the 64-bit PC platform. Hotspot uses a default of 8 for x86 "-server", which means that, for a 1 gig heap, the new generation is limited to 114m and the old generation is limited to 910m. However, for amd64, this value defaults to 2, or 341m for NewGen and 683m for OldGen. Any long-lived object (which isn't an interned string) will make it to the old generation before long, so you can easily get an OOME with 700m worth of non-transient data.

Additional resources:

1 comment:

revathy said...

Very useful Info.Thanks