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"); }