Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Object Memory Footprint - Visualvm and java.sizeOf measurement

I'm trying to understand what is the memory footprint of an object in Java. I read this and other docs on object and memory in Java.

However, when I'm using the sizeof Java library or visualvm, I get two different results where none of them feet what I could expect according to the previous reference (http://www.javamex.com).

For my test, I'm using Java SE 7 Developer Preview on a 64-bits Mac with java.sizeof 0.2.1 and visualvm 1.3.5.

I have three classes, TestObject, TestObject2, TestObject3.

public class TestObject
{

}

public class TestObject2 extends TestObject
{
    int a = 3;
}

public class TestObject3 extends TestObject2
{
    int b = 4;
    int c = 5;
}

My main class:

public class memoryTester
{
    public static void main(String[] args) throws Throwable
    {
        TestObject object1 = new TestObject();
        TestObject2 object2 = new TestObject2();
        TestObject3 object3 = new TestObject3();

        int sum = object2.a + object3.b + object3.c;
        System.out.println(sum);

        SizeOf.turnOnDebug();

        System.out.println(SizeOf.humanReadable(SizeOf.deepSizeOf(object1)));
        System.out.println(SizeOf.humanReadable(SizeOf.deepSizeOf(object2)));
        System.out.println(SizeOf.humanReadable(SizeOf.deepSizeOf(object3)));
    }
}

With java.SizeOf() I get:

{ test.TestObject
} size = 16.0b
16.0b

{ test.TestObject2
 a = 3
} size = 16.0b
16.0b

{ test.TestObject3
 b = 4
 c = 5
} size = 24.0b
24.0b

With visualvm I have:

this (Java frame)   TestObject  #1  16
this (Java frame)   TestObject2 #1  20
this (Java frame)   TestObject3 #1  28

According to documentations I read over Internet, as I'm in 64-bits, I should have an object header of 16 bytes, ok for TestObject.

Then for TestObject2 I should add 4 bytes for the integer field giving the 20 bytes, I should add again 4 bytes of padding, giving a total size of 24 bytes for TestObject2. Am I wrong?

Continuing that way for TestObject3, I have to add 8 bytes more for the two integer fields which should give 32 bytes.

VisualVm seems to ignore padding whereas java.sizeOf seems to miss 4 bytes as if there were included in the object header. I can replace an integer by 4 booleans it gives the same result.

Questions:

Why these two tools give different results?

Should we have padding?

I also read somewhere (I did'nt find back the link) that between a class and its subclass there could be some padding, is it right? In that case, an inherited tree of classes could have some memory overhead?

Finally, is there some Java spec/doc which details what Java is doing?

Thanks for your help.

Update:

To answer the comment of utapyngo, to get the size of the objects in visualvm, I create a heapdump, then in the "Classes" part I check the column "size" next after the column "instances". The number of instances if 1 for each kind of objects.

To answer comment of Nathaniel Ford, I initialized each fieds and then did a simple sum with them in my main method to make use of them. It didn't change the results.

like image 545
Aurélien Avatar asked Mar 19 '13 02:03

Aurélien


1 Answers

Yes padding can happen. It is also possible for objects on the stack to get optimised out entirely. Only the JVM knows the exact sizes at any point in time. As such techniques to approximate the size from within the Java language all tend to disagree, tools that attach to the JVM tend to be the most accurate however. The three main techniques of implementing sizeOf within Java that I am aware of are:

  1. serialize the object and return the length of those bytes (clearly wrong, but useful for relative comparisons)
  2. List item reflection, and hard coded size constants for each field found on an object. can be tuned to be kinda accurate but changes in the JVM and padding that the JVM may or may not be doing will throw it.
  3. List item create loads of objects, run gc and compare changes in jvm heap size

None of these techniques are accurate.

If you are running on the Oracle JVM, on or after v1.5. Then there is a way to read the size of an object straight out of the C structure used by the Java runtime. Not a good idea for production, and get it wrong then you can crash the JVM. But here is a blog post that you may find interesting if you wish to have a go at it: http://highlyscalable.wordpress.com/2012/02/02/direct-memory-access-in-java/

As for documentation on what Java is actually doing, that is JVM specific, version specific and potentially configuration specific too. Each implementation is free to handle objects differently. Even to the extent of optimising objects out entirely, for example, objects that are not passed out from the stack are free not to be allocated on the heap. Some JVMs may even manage to keep the object within the CPU registers entirely. Not your case here, but I include it as an example as to why getting the true size of Java objects is tricky.

So best to take any sizeOf values that you get with a pinch of salt and treat it as a 'guideline' measurement only.

like image 94
Chris K Avatar answered Nov 02 '22 22:11

Chris K