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... args){
    Class<?> theClass = null;
    try {
      theClass = Class.forName(args[0]);
    }
    catch (Exception ex) {
      log("Cannot build a Class object: " + args[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[SAMPLE_SIZE];

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

      long startMemoryUse = getMemoryUse();
      for (int idx=0; idx < objects.length ; ++idx) {
        objects[idx] = aClass.getDeclaredConstructor().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 SAMPLE_SIZE = 100;
  private static long SLEEP_INTERVAL = 100;

  private static void log(String message){  
    System.out.println(message);  
  }
  
  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(SLEEP_INTERVAL);
      System.runFinalization();
      Thread.currentThread().sleep(SLEEP_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 line is a non-null, package-qualified name of a class.
  * @param result is a non-null, empty List which acts as an "out"
  * parameter; when returned, result 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 line, final List<Object> result) {
    Objects.requireNonNull(line);
    Objects.requireNonNull(result);
    if (!result.isEmpty()){
      throw new IllegalArgumentException("Result param must be empty.");
    }

    boolean hasRequestedQuit = 
      line.trim().equalsIgnoreCase(QUIT) ||
      line.trim().equalsIgnoreCase(EXIT)
    ;

    if (hasRequestedQuit) {
      //display only a blank line
      result.add(NEW_LINE);
    }
    else {
      try {
        Class<?> theClass = Class.forName(line);
        ObjectSizer sizer = new ObjectSizer();
        long size = sizer.getObjectSize(theClass);
        if (size > 0){
          Object[] insertedData = {theClass, Long.valueOf(size)};
          MessageFormat sizeMessage = new MessageFormat(PATTERN);
          String message = sizeMessage.format(insertedData);
          result.add(message);
          result.add(NEW_LINE);
        }
        result.add(DEFAULT_PROMPT);
      }
      catch (ClassNotFoundException ex){
        //recover by asking the user for corrected input
        result.clear();
        result.add(ERROR_PROMPT);
      }
    }

    if (result.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 HELLO_PROMPT;
  }

  // PRIVATE
  private static final String HELLO_PROMPT = "Please enter a class name>";
  private static final String DEFAULT_PROMPT = "Please enter a class name>";
  private static final String ERROR_PROMPT = "Invalid.  Example:\"java.lang.String\">";
  private static final String PATTERN = "Approximate size of {0} objects in bytes: {1}";
  private static final String QUIT = "quit";
  private static final String EXIT = "exit";
  private static final String NEW_LINE = System.getProperty("line.separator");
} 

See Also :
Measure application performance
Console input