Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accurate measurement of object sizes

Tags:

java

memory

jvm

The code at the bottom of this question is a bit long but basically creates a few objects and determines their size in memory. I execute the code with the following JVM parameters (TLAB to avoid chunk memory allocation and supposedly get accurate memory usage figures):

-server -Xms2000m -Xmx2000m -verbose:gc -XX:-UseTLAB

I run the code on a 64 bit Hotspot JVM and get the following output:

Java HotSpot(TM) 64-Bit Server VM
Object: 16 bytes

Object with 1 int: 16 bytes
Object with 2 ints: 24 bytes
Object with 3 ints: 24 bytes

Object with 1 long: 24 bytes
Object with 2 longs: 32 bytes
Object with 3 longs: 40 bytes

Object with 1 reference: 16 bytes
Object with 2 references: 24 bytes
Object with 3 references: 24 bytes

I conclude that:

  • an object takes 12 bytes, aligned to 16 bytes.
  • an int takes 4 bytes (1 object with one int is 12 + 4 = still 16 bytes, with 2 ints: 12 + 8 = 20 aligned to 24 bytes)
  • a long takes 8 bytes (1 object with one long is 12 + 8 = 20 bytes, aligned to 24 bytes)

But I struggle to understand why references don't use as much space as longs.

Since references are 8 bytes on a 64-bit JVM, the obvious conclusion is that the measurement methodology has a flaw*. Can you explain what is going on and what can be done to fix it?

*Notes:
- no GC runs during the measurement.
- using the Netbeans profiler yields similar results.

public class TestMemoryReference {

    private static final int SIZE = 100_000;
    private static Runnable r;
    private static Object o = new Object();
    private static Object o1 = new Object();
    private static Object o2 = new Object();
    private static Object o3 = new Object();

    public static class ObjectWith1Int  { int i; }
    public static class ObjectWith2Ints { int i, j; }
    public static class ObjectWith3Ints { int i, j, k; }
    public static class ObjectWith1Long  { long i; }
    public static class ObjectWith2Longs { long i, j; }
    public static class ObjectWith3Longs { long i, j, k; }
    public static class ObjectWith1Object  { Object o = o1; }
    public static class ObjectWith2Objects { Object o = o1; Object p = o2; }
    public static class ObjectWith3Objects { Object o = o1; Object p = o2; Object q = o3; }

    private static void test(Runnable r, String name, int numberOfObjects) {
        long mem = Runtime.getRuntime().freeMemory();
        r.run();
        System.out.println(name + ":" + (mem - Runtime.getRuntime().freeMemory()) / numberOfObjects + " bytes  ");
    }

    public static void main(String[] args) throws Exception {
        System.out.println(System.getProperty("java.vm.name") + "  ");

        r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new Object(); } };
        test(r, "Object", SIZE);

        r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith1Int(); } };
        test(r, "Object with 1 int", SIZE);

        r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith2Ints(); } };
        test(r, "Object with 2 ints", SIZE);

        r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith3Ints(); } };
        test(r, "Object with 3 ints", SIZE);

        r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith1Long(); } };
        test(r, "Object with 1 long", SIZE);

        r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith2Longs(); } };
        test(r, "Object with 2 longs", SIZE);

        r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith3Longs(); } };
        test(r, "Object with 3 longs", SIZE);

        r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith1Object(); } };
        test(r, "Object with 1 reference", SIZE);

        r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith2Objects(); } };
        test(r, "Object with 2 references", SIZE);

        r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith3Objects(); } };
        test(r, "Object with 3 references", SIZE);
    }
}
like image 605
assylias Avatar asked Feb 20 '13 13:02

assylias


People also ask

What is the correct way to measure an object?

Measuring an object is about finding the important dimensions and angles of the object. You use a ruler to measure dimensions and a protractor to measure angles. When you have similar objects, the ratios of their dimensions when comparing one object to the other will be the same for all the dimensions.

What is used to measure the size of an object?

The Instrument used to measure the length of an object is a Ruler.

In what order do dimensions go?

When you tell us the dimensions of the box, they need to be in this order, Length x Width x Depth.


1 Answers

Since references are 8 bytes on a 64-bit JVM

This is your potentially-flawed assumption.

HotSpot is able to use "compressed oops" to use 32-bit values for references in some places of the JVM (emphasis mine):

Which oops are compressed?

In an ILP32-mode JVM, or if the UseCompressedOops flag is turned off in LP64 mode, all oops are the native machine word size.

If UseCompressedOops is true, the following oops in the heap will be compressed:

  • the klass field of every object
  • every oop instance field
  • every element of an oop array (objArray)

I suspect this is what's going on in your case.

Test it by using

-XX:-UseCompressedOops

or

-XX:+UseCompressedOops

On my machine, by default I get the same results as you, but with -XX:-UseCompressedOops I see:

Object:16 bytes
Object with 1 int:24 bytes
Object with 2 ints:24 bytes
Object with 3 ints:32 bytes
Object with 1 long:24 bytes
Object with 2 longs:32 bytes
Object with 3 longs:40 bytes
Object with 1 reference:24 bytes
Object with 2 references:32 bytes
Object with 3 references:40 bytes

... which is probably closer to what you were expecting :)

like image 77
Jon Skeet Avatar answered Oct 14 '22 02:10

Jon Skeet