Reputation: 328608
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 bytesObject with 1 int: 16 bytes
Object with 2 ints: 24 bytes
Object with 3 ints: 24 bytesObject with 1 long: 24 bytes
Object with 2 longs: 32 bytes
Object with 3 longs: 40 bytesObject with 1 reference: 16 bytes
Object with 2 references: 24 bytes
Object with 3 references: 24 bytes
I conclude that:
But I struggle to understand why references don't use as much space as long
s.
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);
}
}
Upvotes: 6
Views: 265
Reputation: 4307
Java Object's size is known when the class is defined.
Memory usage of Java objects: general http://www.javamex.com/tutorials/memory/object_memory_usage.shtml
Upvotes: 0
Reputation: 1500514
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 :)
Upvotes: 6