Rate this page del.icio.us  Digg slashdot StumbleUpon

Use JBoss Profiler to Detect Memory Leaks

by the editorial team

This tip article is edited and republished from the original source.

by Michael Juntao Yuan and Clebert Suconic

Memory leaks are one of the most common bugs in software engineering. A memory leak is created when a developer allocates memory space for an object but forgets to free the memory when the object is no longer in use. Memory leaks are hard to find. But they can cause application crashes, poor performance, or even open security holes if they are not fixed.

Compared with native programming languages such as C and C++, one of the major advantages of the Java platform is that Java has a built-in defense mechanism against memory leaks. The Java Virtual Machine (JVM) runs a Garbage Collector (GC) service periodically to free up memory for objects no long used by the application. So, the developer does not have to remember to the free the objects manually. Automatic memory management in Java is a great feature for improving developer productivity and application quality.

However, Java GC is not the silver bullet for fixing all memory leaks. Contrary to common beliefs, memory leak is still a common problem in Java applications. In Java applications, a memory leak typically occurs when you put an object into a long lived collection, and forget to take it out when you finished using the object. The object reference would stay valid in the collection and ineligible for GC until the collection itself is garbage collected. For instance, in a web application, you might put a object into the HttpSession and forget to set it to null when finished using it. The object would only be garbaged collected when the HttpSession times out or is destroyed by the user. Since the HttpSession can take days to time out, the uncollected objects could take substantial amount of memory if you have many concurrent users. Besides the HttpSession, similar memory leaks can occur in many other places in a Java EE application, including in the cache sub-system, in the persistence sub-system, and anywhere the application state is managed.

While Java memory leaks do not cause security vulnerabilities as C/C++ memory leaks do, they are still a major source of application instability and poor performance. When memory leak happens, the available heap space in the JVM diminishes over time. The JVM has to run GC more frequently and each GC would free less memory until the heap space finally runs out. So, the symptom is that the application would gradually run slower and then the JVM crashes with an OutOfMemory error. However, those symptoms are generic and can be easily caused by other factors: there are many potential reasons why an application would run slow, and OutOfMemory error could occur under heavy load without a memory leak.

Hence, we need to run comprehensive and well-organized load tests to know for sure whether memory leak is the root of the problems. Such tests are often performed at the end of the application development cycle, and that has made memory leaks expensive to find and fix. Or even worse, many Java EE applications never go through comprehensive loading tests in QA and have memory leaks affecting production servers. As developers, we need a tool to find memory leaks early in the application development lifecycle. JBoss Profiler is an open source tool to systematically detect and pinpoint memory leaks in Java EE applications. It helps us monitor detailed memory usage in the application, and integrate memory leak test conditions into unit tests.

Install Software

JBoss Profiler uses the JVM’s native JVMPI (JVM Profiler Interface) and JVMTI (JVM Tool Interface) to gather runtime information (e.g., detailed memory usage) inside the JVM. You must use JDK 5.0 and above to take advantage of JVMPI and JVMTI. The user interacts with the profiler via a web application (jboss-profiler.war). JBoss Profiler services can be started and stopped via Java MBeans deployed in the JBoss JMX management console. So, the JBoss Profiler software consists of native library files, a WAR file for the web application, and JBoss Service ARchive (SAR) files for the MBeans. Please download the JBoss Profiler.

Note
If you do not see a pre-compiled binary of the native library for your platform, you might need to build JBoss Profiler yourself from the source. You would need GCC and JDK 5.0 for the compilation scripts in the download zip bundle to work. Please refer to the JBoss Profiler documentation for more information on how to compile the native libraries.

Installation of JBoss Profiler is very simple:

  • Copy the native library files (i.e., the .so files on Linux / Unix, or the .dll files on Windows) to the directory $LD_LIBRARY_PATH (Unix / Linux) or under the $PATH (Windows).
  • Copy the .sar files for MBeans into the $JBOSS_HOME/server/default/deploy directory.
  • Copy the jboss-profiler.war web application into the $JBOSS_HOME/server/default/deploy directory.
  • Edit your JBoss start script (bin/run.conf for Linux or bin/run.bat for windows) and add flag -agentlib:jbossAgent to the JVM startup parameters.

Now, you can run your application in JBoss AS, and the profiler is ready to use.

Inspect Runtime Memory Usage

To inspect runtime memory usage, you should open the JBoss JMX management console at http://localhost:8080/jmx-console/, and click on the jboss.profiler:mbean=JVMTIClass MBean. Now, click on the invoke button on the inventoryReport operation to create a memory snapshot of the JVM. Figure 1 shows the memory snapshot. It displays all the Java classes in the JVM, how many instances are there for each class, and how much memory each class takes.

Figure 1: The memory snapshot

To monitor for memory leaks, you should put the application under moderate load and wait until it reaches a steady state. Then, check the memory snapshot from time to time. If the memory usage of certain classes, especially Java collection classes, such as List, HashMap, HashTable etc., keep going up, you have a memory leak problem.

Note
The heapSnapshot operation on the jboss.profiler:mbean=JVMTIClass MBean outputs the current heap memory information to an external log file, which can be further analyzed by your own script or by the profiler web application at http://localhost:8080/jboss-profiler/. Using the “memory profiler” in the profiler application, you can read in the log files and see the cross references among classes. For instance, you can view all classes that hold references to the HaspMap objects in the heap, as well as all classes the HashMap objects hold references of.

Compared with other free memory monitoring tools, such as the JConsole inside the JDK, JBoss Profiler outputs detailed class name and cross reference information, which would help you investigate the exact cause of the memory leak.

Pinpoint and Prevent Leaks

Load testing is an invaluable tool to uncover memory leaks. But it does not pinpoint the memory leak location. The best way to deal with memory leaks is to prevent it from occurring altogether. Using JBoss Profiler, we can programatically detect memory leaks in developer unit tests. It allows you to assert that the tested class / function / block of code is free of memory leaks. Below is an example of a unit test case:

public void testMessage() throws Exception {

  JVMTIInterface jvmti = new JVMTIInterface();
  Object obj = TestDomainObject.createTestInstance();
  Map snapshot1=null;
  Map snapshot2=null;

  snapshot1=jvmti.produceInventory();

  // Whatever code you need to test

  snapshot2 = jvmti.produceInventory();

  assertTrue("Produced unexpected memory",
    jvmti.compareInventories(
      System.out, snapshot1, snapshot2, null, null, null));
}

You have to have JBoss Profiler libraries on your classpath to execute this test case. The snapshot1 and snapshot2 variables contain the memory snapshots before and after the tested code runs. If the tested code is free of memory leaks, the two snapshots should be identical. If they are not, the test case would fail and print out the difference between the two snapshots to help you pinpoint the memory leak location.

Detect Integration and Deployer Memory Leaks

With proper memory inventory assertions, we can prevent most memory leaks through unit tests. However, unit tests do not root out memory leaks that occur at integration time or deployment time. For instance, when you deploy and then undeploy an application in JBoss AS, the application classes may not be properly unloaded — some collection classes in the application server still hold references to objects in the undeployed application. This type of memory leak often occurs when you load classes using an external framework outside of the application server (e.g., using the Spring framework). It would cause the application to behave unpredictably when it is re-deployed.

An important sign of unclean undeployment is duplicated classes from different classloaders in the memory heap. You can use the classloader analyzer in the JBoss Profiler web application (http://localhost:8080/jboss-profiler/) to find duplicated classes. The classloader analyzer points you to the exact location in the stack trace where the class reference is still held after undeployment.

Of course, JBoss Profiler does more than memory profiling. It can analyze the application call stacks, and profile the critical path to identify execution bottlenecks and contention points. We will discuss those features in a future article. So stay tuned!

More resources

About the authors

Michael Yuan is the author of four books and a technical evangelist with JBoss. His upcoming book “JBoss Seam: Power and Simplicity Beyond Java EE 5.0″ is the first book on JBoss’s next generation lightweight application framework. Michael has a PhD from the University of Texas at Austin.

Clebert Suconic is a JBoss core developer. He is the project lead for JBoss Profiler and JBoss Serialization. He also contributes to many other JBoss projects including JBoss Messaging and performance tuning of the JBoss Application Server.

Copyright (C) by 2006 Red Hat Inc. This article is licensed under a Creative Commons Attribution 2.5 License (CC BY-SA): http://creativecommons.org/licenses/by-sa/2.5/.

Leave a reply