Monday, July 29, 2013

Compute Java Object Memory Footprint at runtime with JAMM (Java Agent for Memory Measurements)

This short article shows how to measure java object memory size at runtime with JAMM, a java agent dedicated to measure actual object memory use including JVM overhead.

JAMM uses the Instrumentation implementation provided by the JVM to compute memory usage of a given object by calling the getObjectSize(..) method.

It is quite simple to use, as explained by the author:

MemoryMeter meter = new MemoryMeter();
meter.measure(object);
meter.measureDeep(object);
meter.countChildren(object);

The only constraint is to attach JAMM java agent to the JVM before using it to measure memory usage, by starting the JVM with the -javaagent option pointing to the JAMM jar.

So, we are going to write a JUnit that shows how to use JAMM, but before we need to setup maven to achieve this:
    
        
            junit
            junit
            4.11
            test
        
    
        
        
            com.github.stephenc
            jamm
            0.2.5
            test
        
            
    
    
    
        
            
            
            
                org.apache.maven.plugins
                maven-dependency-plugin
                2.8
                
                    
                        copy-dependencies
                        generate-test-resources
                        
                            copy
                        
                        
                            
                                
                                    com.github.stephenc
                                    jamm
                                    0.2.5
                                    jar
                                    ${project.build.directory}
                                    jamm.jar
                                
                            
                        
                    
                
            
            
            
            
                org.apache.maven.plugins
                maven-surefire-plugin
                2.14
                
                    -javaagent:${project.build.directory}/jamm.jar
                
            
            
        
    

Next, write the following JUnit that explores the JAMM features:
package org.javabenchmark.memory;

import java.util.ArrayList;
import java.util.List;
import org.github.jamm.MemoryMeter;
import org.junit.Test;

public class MemoryMeterTest {

    private MemoryMeter meter = new MemoryMeter();
    
    @Test
    public void shouldMeasureMemoryUsage() {

        String st1 = "This is the string #1";
        measure(st1);
        
        String st2 = "This is the string #2 and it has more chars.";
        measure(st2);
        
        List aList = new ArrayList(0);
        measure(aList);
        
        aList.add(st1);
        measure(aList);
        
        aList.add(st2);
        measure(aList);
        
    }

    private void measure(Object anObject) {
        
        System.out.println("-----------------------------------");
        System.out.printf("size: %d bytes\n", meter.measure(anObject));
        System.out.printf("retained size: %d bytes\n", meter.measureDeep(anObject));
        System.out.printf("inner object count: %d\n", meter.countChildren(anObject));
    }
}

Running the test produces the following output on my computer:
-----------------------------------
size: 24 bytes
retained size: 88 bytes
inner object count: 2
-----------------------------------
size: 24 bytes
retained size: 128 bytes
inner object count: 2
-----------------------------------
size: 24 bytes
retained size: 40 bytes
inner object count: 2
-----------------------------------
size: 24 bytes
retained size: 136 bytes
inner object count: 4
-----------------------------------
size: 24 bytes
retained size: 264 bytes
inner object count: 6
To conclude, you can see how it is easy to monitor the memory usage of your objects. It is very handy when dealing with huge collections, or when using caches such as the ones provided by Guava or EHCache. That way you can setup trigger that alert when memory consumption is excessive.


UPDATE: 2013-07-30

To answer to the rxin's comment, i did a quick (trivial) test with a list of 1,000,000 random strings, that represents 116 Mo in memory.

For the test, i sized the heap with the following JVM options: -Xms256m -Xmx256m.

Below is the memory usage during the test:


From my point of view, the memory overhead introduced by JAMM is negligible in that simple test case, but notice that the measureDeep() method takes time (reflection is slow).


 
  

4 comments:

  1. We have been using the following SizeEstimator in the Spark open source project. It is simply using reflection to find all the fields and estimate the sizes of objects.

    https://github.com/mesos/spark/blob/master/core/src/main/scala/spark/SizeEstimator.scala

    Can you comment on how much overhead JAMM brings to the runtime?

    ReplyDelete
    Replies
    1. JAMM and other similar tools like javamex, use java agent to access the Instrumentation class as explained at the beginning of the post. As long as the instrumentation agent does not register any ClassFileTransformer, i do not think that it leads to any performance downside.

      Delete
  2. Hi rxin, Thanks for your comment. I updated the post with some info about JAMM overhead.

    ReplyDelete
  3. This tool does not work

    ReplyDelete