Get size of object in memory

Occasionally, you may be interested in measuring (or estimating) the size of an object in memory. Here's a utility which can help with that task, if the object's class has a no-argument constructor. It follows the style used by Java Platform Performance, by Wilson and Kesselman. Given a class name, it will build a number of objects using the no-argument constructor, and measure the effect on JVM memory use. Thus, it measures the size of 'empty' objects containing no data.

To measure the size of a particular object containing data, a similar technique can easily be used: measure JVM memory use before and after building the object.

In addition, JDK 1.5 has added an Instrumentation interface, which includes a method named getObjectSize method. However, this method seems to be intended for tool makers.


/**
* Measures the approximate size of an object in memory, given a Class which
* has a no-argument constructor.
*/
public final class ObjectSizer {

  /**
  * First and only argument is the package-qualified name of a class
  * which has a no-argument constructor.
  */
  public static void main(String... aArguments){
    Class theClass = null;
    try {
      theClass = Class.forName(aArguments[0]);
    }
    catch (Exception ex) {
      System.err.println("Cannot build a Class object: " + aArguments[0]);
      System.err.println("Use a package-qualified name, and check classpath.");
    }
    long size = ObjectSizer.getObjectSize( theClass );
    System.out.println("Approximate size of " + theClass + " objects :" + size);
  }


  /**
  * Return the approximate size in bytes, and return zero if the class
  * has no default constructor.
  *
  * @param aClass refers to a class which has a no-argument constructor.
  */
  public static long getObjectSize( Class aClass ){
    long result = 0;

    //if the class does not have a no-argument constructor, then
    //inform the user and return 0.
    try {
      aClass.getConstructor( new Class[]{} );
    }
    catch ( NoSuchMethodException ex ) {
      System.err.println(aClass + " does not have a no-argument constructor.");
      return result;
    }

    //this array will simply hold a bunch of references, such that
    //the objects cannot be garbage-collected
    Object[] objects = new Object[fSAMPLE_SIZE];

    //build a bunch of identical objects
    try {
      Object throwAway = aClass.newInstance();

      long startMemoryUse = getMemoryUse();
      for (int idx=0; idx < objects.length ; ++idx) {
        objects[idx] = aClass.newInstance();
      }
      long endMemoryUse = getMemoryUse();

      float approximateSize = ( endMemoryUse - startMemoryUse ) /100f;
      result = Math.round( approximateSize );
    }
    catch (Exception ex) {
      System.err.println("Cannot create object using " + aClass);
    }
    return result;
  }

  // PRIVATE //
  private static int fSAMPLE_SIZE = 100;
  private static long fSLEEP_INTERVAL = 100;

  private static long getMemoryUse(){
    putOutTheGarbage();
    long totalMemory = Runtime.getRuntime().totalMemory();

    putOutTheGarbage();
    long freeMemory = Runtime.getRuntime().freeMemory();

    return (totalMemory - freeMemory);
  }

  private static void putOutTheGarbage() {
    collectGarbage();
    collectGarbage();
  }

  private static void collectGarbage() {
    try {
      System.gc();
      Thread.currentThread().sleep(fSLEEP_INTERVAL);
      System.runFinalization();
      Thread.currentThread().sleep(fSLEEP_INTERVAL);
    }
    catch (InterruptedException ex){
      ex.printStackTrace();
    }
  }
} 

ObjectSizer can be easily placed in an interactive console application.

An example run gives :

>java -cp . Console ObjectSizeInterpreter
Please enter a class name>java.blah
Invalid.  Example:"java.lang.String">java.util.ArrayList
Approximate size of class java.util.ArrayList objects in bytes: 80
Please enter a class name>java.util.Vector
Approximate size of class java.util.Vector objects in bytes: 80
Please enter a class name>java.util.HashMap
Approximate size of class java.util.HashMap objects in bytes: 104
Please enter a class name>java.lang.Long
class java.lang.Long does not have a no-argument constructor.
Please enter a class name>exit

Bye.

Here is the Interpreter class :


import java.util.*;
import java.text.MessageFormat;

/**
* Given a package-qualified class name, return the approximate size of
* the object in bytes.
*/
public final class ObjectSizeInterpreter implements Interpreter {

  /**
  * @param aLine is a non-null, package-qualified name of a class.
  * @param aResult is a non-null, empty List which acts as an "out"
  * parameter; when returned, aResult must contain a non-null, non-empty
  * List containing a description of the size of the object.
  *
  * @return true only if the user has requested to quit the Interpreter.
  * @exception IllegalArgumentException if a param does not comply.
  */
  public boolean parseInput (String  aLine, final List aResult) {
    if ( aResult == null ) {
      throw new IllegalArgumentException("Result param cannot be null.");
    }
    if ( !aResult.isEmpty() ){
      throw new IllegalArgumentException("Result param must be empty.");
    }
    if ( aLine == null ) {
      throw new IllegalArgumentException("Line must not be null.");
    }

    boolean hasRequestedQuit = aLine.trim().equalsIgnoreCase(fQUIT) ||
                               aLine.trim().equalsIgnoreCase(fEXIT);
    if ( hasRequestedQuit ) {
      //display only a blank line
      aResult.add(fNEW_LINE);
    }
    else {
      try {
        Class theClass = Class.forName(aLine);
        long size = ObjectSizer.getObjectSize( theClass );
        if ( size > 0 ){
          Object[] insertedData = {theClass, new Long(size) };
          MessageFormat sizeMessage = new MessageFormat(fPATTERN);
          String message = sizeMessage.format( insertedData );
          aResult.add( message );
          aResult.add( fNEW_LINE );
        }
        aResult.add( fDEFAULT_PROMPT );
      }
      catch (ClassNotFoundException ex){
        //recover by asking the user for corrected input
        aResult.clear();
        aResult.add(fERROR_PROMPT);
      }
    }

    if ( aResult.isEmpty() ) {
      throw new IllegalStateException("Result must be non-empty.");
    }
    return hasRequestedQuit;
  }

  /**
  * Return the text to be displayed upon start-up of the Interpreter.
  */
  public String getHelloPrompt() {
    return fHELLO_PROMPT;
  }

  /// PRIVATE /////
  private static final String fHELLO_PROMPT = "Please enter a class name>";
  private static final String fDEFAULT_PROMPT = "Please enter a class name>";
  private static final String fERROR_PROMPT = "Invalid.  Example:\"java.lang.String\">";
  private static final String fPATTERN = "Approximate size of {0} objects in bytes: {1}";
  private static final String fQUIT = "quit";
  private static final String fEXIT = "exit";
  private static final String fNEW_LINE = System.getProperty("line.separator");
} 



See Also :
Measure application performance
Console input
Would you use this technique?
Yes   No   Undecided   
© 2013 Hirondelle Systems | Source Code | Contact | License | RSS
Individual code snippets can be used under this BSD license - Last updated on August 30, 2012.
Over 2,400,000 unique IPs last year - Built with WEB4J.
- In Memoriam : Bill Dirani -