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);
}
}
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.
The Instrument used to measure the length of an object is a Ruler.
When you tell us the dimensions of the box, they need to be in this order, Length x Width x Depth.
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 :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With