Posts Tagged memory

HOWTO: Determine the size of a Java Object or Class

Finding out the size (memory/heap usage) of Java Objects and Classes can be somewhat tricky as there is no built-in sizeof()-like functionality.  Luckily most people don’t have to face this problem.  But when you do, it can be difficult and in the end is almost always a best-guess approximation.

The reason that I started to look in to this subject was to build a memory-aware, LRU eviction cache for the open source database project HBase.  You can see the final result of that effort here.

When sizing a Class you can take two approaches.  One is to use the methods totalMemory and freeMemory from the Java Runtime class. The other strategy is to analyze the internal structure of your Class.  The first approach has the benefit that it gives you the actual memory footprint of your object rather than the expected one, but has the downside of relying on the garbage collector to do it’s thing.  So for this to work you often have to make repeated calls to the GC and sleep in between to give it time to process and minimize heap usage.  As one can see, this is not a solution suitable to be used in real time system, but can be a solid solution that you go back to when everything else fails, or just to verify your findings.

The second way of sizing your Class is to dissect it into it’s fundamental building blocks.  Let’s start by introducing the notion of a reference, known in other languages as a pointer.  A reference, or REF for short, is 4 bytes on a 32 bit system and 8 bytes on a 64 bit system.  Also note, the total size of an Object will always be word-aligned (4 byte aligned on 32bit, 8 byte aligned on 64bit).

Every instantiated Object in Java has a fixed overhead of 2 REFs.  To that, you add the contributions from all the non-static variables/fields contained within the Object.  For the most basic primitives, these sizes are:

byte = 1 byte, int = 4 bytes, long = 8 bytes

For example, let’s define and size the Class PrimitiveContainer

public class PrimitiveContainer {
  public int x = 1;
  public long y = 2;
}

The calculation for this would be:

overhead + sizeof(int) + sizeof(long) = (2 * REF) + 4 + 8 = 28 bytes on 64-bit, after word-alignment we get 32 bytes

Another example that uses Objects instead of primitives:

public class ObjectContainer {
  public Integer x = 1;
  public Long y = 2;
}

Since this Object contains references to other Objects (not primitives), we will calculate and include both the size of the referenced Objects (Integer and Long) as well as the references to those Objects (in ObjectContainer).  Both Integer and Long will have the fixed Object overhead of 2 * REF in addition to either an int or long primitive in each.  So:

sizeof(Integer) = (2 * REF) + 4 = 20
sizeof(Long) = (2 * REF) + 8 = 24

The total calculation would be:

overhead + reference to Integer + sizeof(Integer) + reference to Long + sizeof(Long) = (2 * REF) + (REF + 20) + (REF + 24) = 80 bytes

As you can see that there is a significant increase in memory usage between using primitives when compared to their Object counterparts, in this case 2.5X larger.

Arrays in Java, though underneath are really Objects, are unlike other complex Classes (Lists, Maps, etc) because they can be used with primtives directly.  Through experimentation and instrumentation of the JVM, the fixed overhead of any array is 3 * REF.

So with this in mind you can treat it as a regular object that aligns with it’s daughter objects.

For example if you have:

byte [] bs = {1,2,3,4};

on a 32 bit system, you get a total size of : 3 * REF + 4 = 3 * 4 + 4 = 16 bytes
on a 64 bit system, you get a total size of:  3 * REF + 4 = 3 * 8 + 4 = 28 which aligns to 32 bytes (twice the memory usage in this case)

One more thing that is worth mentioning is the cost for static and final variables. Usually static variables are not taken into consideration when sizing you object, since this is only done once and not for each instantiated Object.  Final variables should almost always be included, but of course it depends on your use case and the reason that you are sizing your objects.

, , , , , , ,

9 Comments