Java Memory Reloaded

We can basically distinguish memory areas that are available for all threads in a JVM and those memory areas that are exclusively accessible from only one thread. The two areas that are available from all threads are the Method Area and the Heap.

The method area is responsible for storing class information. The Class-Loader will load the bytecode of a class and will pass it to the JVM. The JVM will generate an internal class representation of the bytecode and store it in the method area. The internal representation of a class will have the following data areas:

  • Runtime Constant Pool Numeric constants of the class of types int, long, float or double, String-constants and symbolic references to all methods, attributes and types of this class.
  • Method CodeThe implementation (code) of all methods of this class including constructors etc.
  • Attributes A list of all named attributes of this class.
  • FieldsValues of all fields of this class as references to the Runtime Constant Pool.
The method area can be part of the heap and will be created at runtime. The size of the method area can be static or dynamic and it does not have to provide a Garbage Collector.

 

The method area is implemented as a separated part: The Permanent Generation.

 PermGen is where the JVM stores classes. Classes are stored in PermGen using class name and class loader as the unique identifier. Each web application has its own class loader; this allows different versions of the same class (with the same name) to be used in different web applications without conflict. A web application also gets a new class loader when it is reloaded. Classes are removed from PermGen when the class loader is collected by the garbage collector. This normally happens after a web application stops. However, if something retains a reference to the web application class loader, it won't be garbage collected and the classes it loaded will remain in PermGen. Since PermGen is limited in size, this usually only has to happen a few times before all the PermGen is used. That is when the OOME occurs.


To prevent the OOME, it is necessary to ensure that nothing retains a reference to the web application class loader.

The second memory area that is available for all threads inside the JVM is the Heap. The Java heap manages instances of classes (objects) and arrays at runtime. The heap will be created at JVM startup and the size can be static or dynamic. The JVM specification mandates a Garbage Collection mechanism for reclaiming the memory of an object on the Java heap. The implementation of the Garbage Collector is not specified, but it is not allowed to provide the programmer with an explicit mechanism for deallocating the memory of an object.

Lets have a look at the Sun HotSpot implementation as an example:

The heap is devided into two generations: The Young Generation and the Tenured Generation.

 

 

GC Roots:

  • current stack of all live threads,
  • static members of loaded classes,

 

public class MyFrame extends javax.swing.JFrame {
 
  // reachable via Classloader as soon class is loaded
  public static final ArrayList STATIC = new ArrayList();
 
  // as long as the JFrame is not dispose()'d,
  // it is reachable via a native window
  private final ArrayList jni = new ArrayList()
 
  // while this method is executing parameter is kept reachable,
  // even if it is not used
  private void myMethod(ArrayList parameter) {
 
    // while this method is running, this list is reachable from the stack
    ArrayList local = new ArrayList();
  }
 
}

  

Typical causes are static collections, which are used as a kind of cache. Usually objects are added, but never removed (Let’s face it: How often have you used add() and put() and how often used remove() methods?). Because the objects are referenced by the static collection, they cannot be freed up anymore, as the collection has a GC root reference via the classloader.

But the so called Retained Heap is more important for finding leaks, as it is the total size of all objects being kept alive by this dominator. To get this number, MAT needs to calculate the Retained Size by following all object references. As sorting by Retained Heap is very helpful for finding leaks, MAT displays the largest retained heaps in a pie chart.

 To determine who is creating these objects, or find out what the purpose of some structures is, the actual instances with their incoming and outgoing references are required. To get them, choose List Objects with incoming References from the context menu. Now a tree structure is displayed, showing all instances with all incoming references. Those references keep the object alive and prevented them from being garbage collected. Outgoing References are interesting as well, because they show the actual contents of the instances, helping to find out their purpose. 

Eclipse MAT allows comparing two heapdump snapshots and finding growing structures and instance counts. But this requires two well timed dumps. With some amount of load they should be minutes apart, with few load hours or days. Having a greater time lag helps in separating normal jitter from real issues. 

 

OOME 

The memory leaks all fit a common pattern. The web application creates an object of a class loaded by the web application class loader and then places this object in a registry that has been loaded by a different class loader. This is fine, as long as the object is removed from the registry when the web application stops. If the object is not removed, the registry retains a reference to the object, the object retains a reference to the class and the class retains a reference to the class loader. This pins the class loader in memory. This chain of references prevents the class loader from being garbage collected which in turn means that the PermGen used to store classes loaded by this class loader cannot be freed.

 

Servlet and J2EE containers have a clear purpose: to provide a set of services to independent and isolated components. That set of services is defined by the servlet and j2ee specifications. These specifications also define a mechanism for components to provide any libraries they depend on -- WEB-INF/lib.

It is therefore a complete mystery to me why people seem so keen to push libraries up out of the components where they belong and into the container's library directories. It's like pushing user code into the operating system kernel. Just don't do it.

A component naturally has references to objects in the container; the fact that the container provides classes (which are objects) that are visible to the component is the most obvious example. This causes no problems for garbage-collecting of the component. Problems come when the links become bidirectional, and classes within the container also have references to classes and objects within the components. The servlet and j2ee frameworks are carefully designed to avoid this where possible; where that is unavoidable the container *knows* about those situations and ensures the necessary references are cleared when a component is undeployed .

 

[1] Except for the brain-dead design of JDBC where jdbc drivers loaded via a custom classloader apparently get stored in a map within java.sql.DriverManager thereby causing cyclic references to that classloader.

[2] And except for the cache of classes held by the java.beans.Introspector class. In at least some versions of java, introspecting a class loaded by component's classloader causes a strong reference to that class to be put into a global cache. This means that the component's classloader is then prevented from being unloaded. A workaround is to call java.beans.Introspector.flushCaches() when the component is unloaded. Some containers may do this automatically; Apache Tomcat 5.x certainly does. In other cases, a ServletContextListener may need to be registered to force this to be done.

 

  ThreadLocal<Context[]> threadLocal =
new ThreadLocal<Context[]>() {
protected Context[] initialValue() {
return new Context[1];
}
};

void doSomethingInContext() {
Context[] c = threadLocal.get();
if (c[0] == null) {
try {
c[0] = new Context();
doSomething(c[0]);
}
finally {
c[0] = null;
}
} else {
doSomething(c[0]);
}
}

If we don't want our ThreadLocal instance to prevent the garbage collection of the Context class, we should use an Object[] instead of a Context[]. We might want to do this if library code (in the system classpath perhaps) references our thread local variable and a child class loader loads Context.

 

Sample

Configuration

To have a chance to find something during a post-mortem analysism, one should always use the JVM parameter -XX:+HeapDumpOnOutOfMemoryError.

 

-XX:MinHeapFreeRation=

  • Default is 40 (40%)
  • When the JVM allocates memory, it allocates enough to get 40% free
  • Huge chunks, very large default
  • Not important when -Xms==-Xmx

 

-XX:MaxHeapFreeRation=

  • Default 70%
  • To avoid over allocation
  • To give back memory not used

Young generation | Old generation

  • a good size for the YG is 33% of the total heap
  • Young generation 
  • All new objects are created here
  • Only move to Old gen if they survive one or more minor GC
  • Sized using
  1. -Xmn  -not preferred (fixed value)
  2. -XX:NewRatio=  --preferred (dynamic)

Survivor Spaces

  • 2, used during the GC Algorithm(minor collections)

Young Size (-XX:NewRatio)

Survivor Ratio (-XX:SurvivorRatio)

Eden space | Survivor space

new objects| TO FROM(64K default)

 

OLD Generation | Tenured space

(5MB MIN 44MB MAX)default

 

-XX:PermSize (initial)

-XX:MaxPermSize (max)

 

-XX:SoftRefLRUPolicyMSPerMB

posted on 2012-03-15 10:12  grep  阅读(298)  评论(0编辑  收藏  举报