Skip to content
mchr3k edited this page Jan 10, 2011 · 42 revisions

InMemProfiler is a Java tool which tracks memory allocations. A number of modes can be used to help track down

  • How many instances of a class are being allocated?
  • What are the most common stack traces of these allocations?
  • How long do these instances live?
  • What class is responsible for their allocation?

Why?

Java makes it really easy to write code which allocates a lot of short lived memory (e.g. String concatenation) or to use memory structures which are larger than you might expect (e.g. Vectors). This tool allows you to quickly see find out more details about the number, size and source of object allocations.

Example output

Live objects:
  56:1 - [Ljava.lang.Object;
  24:1 - java.util.ArrayList

Live objects summary:
  80:2

Collected objects:
  0(s) - 5(s) :
    440:6 - [Ljava.lang.Object;
    24:1 - java.util.ArrayList
    8:1 - other.test.TestObject
  5(s) - 15(s) :
    80:10 - other.test.TestObject
    56:1 - [Ljava.lang.Object;
    24:1 - java.util.ArrayList
  15(s) - 25(s) :
    160:20 - other.test.TestObject
    112:1 - [Ljava.lang.Object;
    24:1 - java.util.ArrayList
  25(s) - 35(s) :
    240:30 - other.test.TestObject
    168:1 - [Ljava.lang.Object;
    24:1 - java.util.ArrayList
  35(s) - 45(s) :
  45(s) - 55(s) :
  55(s) - inf(s) :

  Buckets summary:
    472:8 - 0(s) - 5(s)
    160:12 - 5(s) - 15(s)
    296:22 - 15(s) - 25(s)
    432:32 - 25(s) - 35(s)
    0:0 - 35(s) - 45(s)
    0:0 - 45(s) - 55(s)
    0:0 - 55(s) - inf(s)

This output is generated from the following test program:

package test;

import java.util.ArrayList;
import java.util.List;

import other.test.TestObject;

public class Test
{
  public static void main(String[] args) throws Exception
  {
    // Allocate 30 objects
    List<TestObject> round1 = allocateObjects(30);
    Thread.sleep(10 * 1000);

    // Allocate 20 objects
    List<TestObject> round2 = allocateObjects(20);
    Thread.sleep(10 * 1000);

    // Allocate 10 objects
    List<TestObject> round3 = allocateObjects(10);
    Thread.sleep(10 * 1000);

    // Allocate 1 object, discarding 30 which are 30 seconds old
    round1 = allocateObjects(1);
    System.out.println(round1);

    // Discard 1 object which is 0 seconds old
    round1 = null;
    System.out.println(round2);

    // Discard 20 objects which are 20 seconds old
    round2 = null;
    System.out.println(round3);

    // Discard 30 objects which are 30 seconds old
    round3 = null;
    Thread.sleep(6 * 1000);
    System.out.println("===");
  }

  private static List<TestObject> allocateObjects(int len)
  {
    List<TestObject> list = new ArrayList<TestObject>();
    for (int ii = 0; ii < len; ii++)
    {
      list.add(new TestObject());
    }
    return list;
  }
}

Note: The extra ArrayList instance alive at the end of this code is created by a JVM system class. This will become clearer below.

This output is made possible by adding the following arguments to the JVM used to run this program.

-Xbootclasspath/a:./inmemprofiler.jar -agentpath:./inmemprofiler.dll=#bucket-5,15,25,35,45,55#gc-1#include-other,[Ljava.lang.Object,java.util.ArrayList#trackcollection

To find out more about the source of these objects you can use the #trace option to generate the following output:

Live objects:
  56:1 - [Ljava.lang.Object;

    Allocation Sites:
      56:1
        java.util.ArrayList.<init>(ArrayList.java)
        java.util.ArrayList.<init>(ArrayList.java)
        java.io.FilePermissionCollection.<init>(FilePermission.java)
        java.io.FilePermission.newPermissionCollection(FilePermission.java)
        java.security.Permissions.getPermissionCollection(Permissions.java)
        java.security.Permissions.add(Permissions.java)
        java.net.URLClassLoader.getPermissions(URLClassLoader.java)
        sun.misc.Launcher$AppClassLoader.getPermissions(Launcher.java)
        java.security.SecureClassLoader.getProtectionDomain(SecureClassLoader.java)
        java.security.SecureClassLoader.defineClass(SecureClassLoader.java)
        java.net.URLClassLoader.defineClass(URLClassLoader.java)
        java.net.URLClassLoader.access$000(URLClassLoader.java)
        java.net.URLClassLoader$1.run(URLClassLoader.java)
        java.security.AccessController.doPrivileged(AccessController.java)
        java.net.URLClassLoader.findClass(URLClassLoader.java)
        java.lang.ClassLoader.loadClass(ClassLoader.java)
        sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java)
        java.lang.ClassLoader.loadClass(ClassLoader.java)

    Allocation Classes:
      java.security.Permissions
        56:1 - All methods
          56:1 - add
          56:1 - getPermissionCollection
      java.security.SecureClassLoader
        56:1 - All methods
          56:1 - getProtectionDomain
          56:1 - defineClass
      sun.misc.Launcher$AppClassLoader
        56:1 - All methods
          56:1 - loadClass
          56:1 - getPermissions
      java.io.FilePermission
        56:1 - All methods
          56:1 - newPermissionCollection
      java.lang.ClassLoader
        56:1 - All methods
          56:1 - loadClass
      java.net.URLClassLoader
        56:1 - All methods
          56:1 - access$000
          56:1 - findClass
          56:1 - defineClass
          56:1 - getPermissions
      java.util.ArrayList
        56:1 - All methods
          56:1 - <init>
          0:0 - add
          0:0 - ensureCapacity
      java.security.AccessController
        56:1 - All methods
          56:1 - doPrivileged
      java.net.URLClassLoader$1
        56:1 - All methods
          56:1 - run
      java.io.FilePermissionCollection
        56:1 - All methods
          56:1 - <init>

  24:1 - java.util.ArrayList

    Allocation Sites:
      24:1
        java.util.ArrayList.<init>(ArrayList.java)
        java.io.FilePermissionCollection.<init>(FilePermission.java)
        java.io.FilePermission.newPermissionCollection(FilePermission.java)
        java.security.Permissions.getPermissionCollection(Permissions.java)
        java.security.Permissions.add(Permissions.java)
        java.net.URLClassLoader.getPermissions(URLClassLoader.java)
        sun.misc.Launcher$AppClassLoader.getPermissions(Launcher.java)
        java.security.SecureClassLoader.getProtectionDomain(SecureClassLoader.java)
        java.security.SecureClassLoader.defineClass(SecureClassLoader.java)
        java.net.URLClassLoader.defineClass(URLClassLoader.java)
        java.net.URLClassLoader.access$000(URLClassLoader.java)
        java.net.URLClassLoader$1.run(URLClassLoader.java)
        java.security.AccessController.doPrivileged(AccessController.java)
        java.net.URLClassLoader.findClass(URLClassLoader.java)
        java.lang.ClassLoader.loadClass(ClassLoader.java)
        sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java)
        java.lang.ClassLoader.loadClass(ClassLoader.java)

    Allocation Classes:
      java.security.Permissions
        24:1 - All methods
          24:1 - add
          24:1 - getPermissionCollection
      java.security.SecureClassLoader
        24:1 - All methods
          24:1 - getProtectionDomain
          24:1 - defineClass
      sun.misc.Launcher$AppClassLoader
        24:1 - All methods
          24:1 - loadClass
          24:1 - getPermissions
      java.io.FilePermission
        24:1 - All methods
          24:1 - newPermissionCollection
      java.lang.ClassLoader
        24:1 - All methods
          24:1 - loadClass
      java.net.URLClassLoader
        24:1 - All methods
          24:1 - access$000
          24:1 - findClass
          24:1 - defineClass
          24:1 - getPermissions
      java.util.ArrayList
        24:1 - All methods
          24:1 - <init>
      java.security.AccessController
        24:1 - All methods
          24:1 - doPrivileged
      java.net.URLClassLoader$1
        24:1 - All methods
          24:1 - run
      java.io.FilePermissionCollection
        24:1 - All methods
          24:1 - <init>


Live objects summary:
  80:2

Collected objects:
  0(s) - 60(s) :
    776:9 - [Ljava.lang.Object;

      Allocation Sites:
        552:5
          java.util.Arrays.copyOf(Arrays.java)
          java.util.Arrays.copyOf(Arrays.java)
          java.util.ArrayList.ensureCapacity(ArrayList.java)
          java.util.ArrayList.add(ArrayList.java)
          test.Test.allocateObjects(Test.java)
          test.Test.main(Test.java)
        224:4
          java.util.ArrayList.<init>(ArrayList.java)
          java.util.ArrayList.<init>(ArrayList.java)
          test.Test.allocateObjects(Test.java)
          test.Test.main(Test.java)

      Allocation Classes:
        java.util.ArrayList
          776:9 - All methods
            552:5 - add
            552:5 - ensureCapacity
            224:4 - <init>
        test.Test
          776:9 - All methods
            776:9 - allocateObjects
            776:9 - main
        java.util.Arrays
          552:5 - All methods
            552:5 - copyOf

    488:61 - other.test.TestObject

      Allocation Sites:
        488:61
          test.Test.allocateObjects(Test.java)
          test.Test.main(Test.java)

      Allocation Classes:
        test.Test
          488:61 - All methods
            488:61 - allocateObjects
            488:61 - main

    96:4 - java.util.ArrayList

      Allocation Sites:
        96:4
          java.util.ArrayList.<init>(ArrayList.java)
          test.Test.allocateObjects(Test.java)
          test.Test.main(Test.java)

      Allocation Classes:
        java.util.ArrayList
          96:4 - All methods
            96:4 - <init>
        test.Test
          96:4 - All methods
            96:4 - allocateObjects
            96:4 - main

  60(s) - inf(s) :

  Buckets summary:
    1360:74 - 0(s) - 60(s)
    0:0 - 60(s) - inf(s)

This was generated with the following set of options:

-Xbootclasspath/a:./inmemprofiler.jar -agentpath:./inmemprofiler.dll=#bucket-60#gc-1#include-other,[Ljava.lang.Object,java.util.ArrayList#trackcollection#trace

This output clearly shows that the extra ArrayList instance and corresponding Object[] comes from a call to URLClassLoader.getPermissions during classloading.

Usage

To use this tool you should download the following two files (select the appropriate dll or so file) into your application’s working directory:

You then need to add the following arguments to your java command.

Windows

-Xbootclasspath/a:./inmemprofiler.jar -agentpath:./inmemprofiler.dll

Linux

-Xbootclasspath/a:./inmemprofiler.jar -agentpath:./libinmemprofiler.so

You can specify arguments to the agent in string form e.g.

-Xbootclasspath/a:./inmemprofiler.jar -agentpath:./inmemprofiler.dll=#bucket-5,15,25,35,45,55#include-other,java.util.ArrayList#gc-1

The full list of options is described on the Profiler Options page.

Clone this wiki locally