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) {
      log("Cannot build a Class object: " + aArguments[0]);
      log("Use a package-qualified name, and check classpath.");
    }
    ObjectSizer sizer = new ObjectSizer();
    long size = sizer.getObjectSize(theClass);
    log("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 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) {
      log(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) {
      log("Cannot create object using " + aClass);
    }
    return result;
  }

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

  private static void log(String aMessage){  
    System.out.println(aMessage);  
  }
  
  private long getMemoryUse(){
    putOutTheGarbage();
    long totalMemory = Runtime.getRuntime().totalMemory();
    putOutTheGarbage();
    long freeMemory = Runtime.getRuntime().freeMemory();
    return (totalMemory - freeMemory);
  }

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

  private 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<Object> 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);
        ObjectSizer sizer = new ObjectSizer();
        long size = sizer.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   
© 2014 Hirondelle Systems | Source Code | Contact | License | RSS
Individual code snippets can be used under this BSD license - Last updated on September 21, 2013.
Over 2,000,000 unique IPs last year - Built with WEB4J.
- In Memoriam : Bill Dirani -