The object header contains metadata such as the identity hash, mark, and klass. Hexdumping the memory of a simple object before and after getting the identity hash shows the hash being written to the header. On 64-bit JVMs, the header is 8 bytes, containing unused space, hash, and mark fields. On 32-bit JVMs, the header is 4 bytes, with the hash overwriting unused space after being set.
13. Objects
public class MyClass {
public boolean field1;
public int field2;
public Object field3;
}
What's sizeof(new MyClass())?
14. Objects
public class MyClass {
public boolean field1;
public int field2;
public Object field3;
}
What's sizeof(new MyClass())?
This is considered VM implementation and hardware detail!
(But sometimes we'd like to know.)
16. Low-level Java
Read the VM source code.
If available (openjdk). Requires time.
Instrumentation (agent).
Provides getObjectSize estimate. Requires an agent at startup.
sun.misc.Unsafe
SUN's box of "only we can touch it" toys. Inherited by most other vendors (be-
cause of sublicensing, for compatibility?).
18. WARN: any of the code shown further on relies on
undocumented and unspeci ed JVM internals and may not
work or even compile.
19. Acquiring Unsafe instance like this:
sun.misc.Unsafe.getUnsafe();
java.lang.SecurityException: Unsafe
at sun.misc.Unsafe.getUnsafe(Unsafe.java:68)
at com.carrotsearch.sizeofexamples.UnsafeAccess.unsafe(UnsafeAccess.java:8)
...
20. Acquiring Unsafe instance like this:
sun.misc.Unsafe.getUnsafe();
java.lang.SecurityException: Unsafe
at sun.misc.Unsafe.getUnsafe(Unsafe.java:68)
at com.carrotsearch.sizeofexamples.UnsafeAccess.unsafe(UnsafeAccess.java:8)
...
.. . is a no-no. But we can access the static singleton:
Class<?> unsafeClass = sun.misc.Unsafe.class;
Field fld = unsafeClass.getDeclaredField("theUnsafe");
fld.setAccessible(true);
return (sun.misc.Unsafe) fld.get(null);
21. Here's a snippet of Unsafe documentation:
/**
* Fetches a value from a given Java variable.
*
* More specifically, fetches a field or array element within the given
* object <code>o</code> at the given offset, or (if <code>o</code> is
* null) from the memory address whose numerical value is the given
* ...
*/
public native int getByte(Object o, long offset);
22. Here's a snippet of Unsafe documentation:
/**
* Fetches a value from a given Java variable.
*
* More specifically, fetches a field or array element within the given
* object <code>o</code> at the given offset, or (if <code>o</code> is
* null) from the memory address whose numerical value is the given
* ...
*/
public native int getByte(Object o, long offset);
How about if we hexdump around and beyond object o?
23. public static class MyClass {
public byte field;
public MyClass(byte v) { field = v; }
}
@Test
public void simpleHexDumpByte() {
Object o1 = new MyClass((byte) 0xFE);
Object o2 = new MyClass((byte) 0xFA);
Object o3 = new MyClass((byte) 0xF0);
Unsafe unsafe = UnsafeAccess.unsafe();
for (long i = 0; i < 64;) {
System.out.print(String.format("%02x ", unsafe.getByte(o1, i) & 0xFF));
if ((++i % 16) == 0) System.out.println();
}
System.out.println();
}
.
E01 Alignments .
.
.
24. public static class MyClass {
public byte field;
public MyClass(byte v) { field = v; }
}
@Test
public void simpleHexDumpByte() {
Object o1 = new MyClass((byte) 0xFE);
Object o2 = new MyClass((byte) 0xFA);
Object o3 = new MyClass((byte) 0xF0);
Unsafe unsafe = UnsafeAccess.unsafe();
for (long i = 0; i < 64;) {
System.out.print(String.format("%02x ", unsafe.getByte(o1, i) & 0xFF));
if ((++i % 16) == 0) System.out.println();
}
System.out.println();
}
-- simpleHexDumpByte(com.carrotsearch.sizeofexamples.E01_Alignments)
01 00 00 00 00 00 00 00 60 a5 21 bd fe 00 00 00
01 00 00 00 00 00 00 00 60 a5 21 bd fa 00 00 00
01 00 00 00 00 00 00 00 60 a5 21 bd f0 00 00 00
01 00 00 00 00 00 00 00 f0 28 e1 bc 28 6d 88 eb
.
E01 Alignments .
.
.
25. public static class MyClassInt {
public int field;
public MyClassInt(int v) { field = v; }
}
@Test
public void simpleHexDumpInt() {
Object o1 = new MyClassInt(0xAABBCCDD);
Object o2 = new MyClassInt(0x11223344);
Unsafe unsafe = UnsafeAccess.unsafe();
for (long i = 0; i < 64;) {
System.out.print(String.format("%02x ", unsafe.getByte(o1, i) & 0xFF));
if ((++i % 16) == 0) System.out.println();
}
System.out.println();
}
.
E01 Alignments .
.
.
26. public static class MyClassInt {
public int field;
public MyClassInt(int v) { field = v; }
}
@Test
public void simpleHexDumpInt() {
Object o1 = new MyClassInt(0xAABBCCDD);
Object o2 = new MyClassInt(0x11223344);
Unsafe unsafe = UnsafeAccess.unsafe();
for (long i = 0; i < 64;) {
System.out.print(String.format("%02x ", unsafe.getByte(o1, i) & 0xFF));
if ((++i % 16) == 0) System.out.println();
}
System.out.println();
}
-- simpleHexDumpInt(com.carrotsearch.sizeofexamples.E01_Alignments)
01 00 00 00 00 00 00 00 08 f6 21 bd dd cc bb aa
01 00 00 00 00 00 00 00 08 f6 21 bd 44 33 22 11
01 00 00 00 00 00 00 00 f0 df e5 bc 01 00 00 00
00 00 00 00 1a 00 00 00 78 1f ed bc 70 b1 21 bd
.
E01 Alignments .
.
.
27. Endianness (byte order)
Affects multi-byte types
Intels typically little-endian
Other processors big-endian or bi-endian
You can't assume anything about byte order
But it is veri able -- java.nio.ByteOrder.
28. Conclusions so far
Object o1 = new MyClass((byte) 0xFE);
Object o2 = new MyClass((byte) 0xFA);
Object o3 = new MyClass((byte) 0xF0);
-- simpleHexDumpByte(com.carrotsearch.sizeofexamples.E01_Alignments)
01 00 00 00 00 00 00 00 60 a5 21 bd fe 00 00 00
01 00 00 00 00 00 00 00 60 a5 21 bd fa 00 00 00
01 00 00 00 00 00 00 00 60 a5 21 bd f0 00 00 00
01 00 00 00 00 00 00 00 f0 28 e1 bc 28 6d 88 eb
What is before elds?
Where does each eld start/end?
What is the space "in between" objects?
35. on a 64-bit JVM:
-- hashHeader(com.carrotsearch.sizeofexamples.E02_Header)
Clean. [JVM: Java HotSpot(TM) 64-Bit Server VM, 21.0-b17, 1.7.0]
0x0000 01 00 00 00 00 00 00 00 d8 dc 1b bd fe 00 00 00
After identity hash: 7bf90a55
0x0000 01 55 0a f9 7b 00 00 00 d8 dc 1b bd fe 00 00 00
36. on a 64-bit JVM:
-- hashHeader(com.carrotsearch.sizeofexamples.E02_Header)
Clean. [JVM: Java HotSpot(TM) 64-Bit Server VM, 21.0-b17, 1.7.0]
0x0000 01 00 00 00 00 00 00 00 d8 dc 1b bd fe 00 00 00
After identity hash: 7bf90a55
0x0000 01 55 0a f9 7b 00 00 00 d8 dc 1b bd fe 00 00 00
on a 32-bit JVM:
-- hashHeader(com.carrotsearch.sizeofexamples.E02_Header)
Clean. [JVM: Java HotSpot(TM) Server VM, 11.0-b16, 1.6.0_11]
0x0000 01 00 00 00 90 5c 3d b2 fe 00 00 00 00 00 00 00
After identity hash: 005e176f
0x0000 81 b7 0b 2f 90 5c 3d b2 fe 00 00 00 00 00 00 00
37. on a 64-bit JVM:
-- hashHeader(com.carrotsearch.sizeofexamples.E02_Header)
Clean. [JVM: Java HotSpot(TM) 64-Bit Server VM, 21.0-b17, 1.7.0]
0x0000 01 00 00 00 00 00 00 00 d8 dc 1b bd fe 00 00 00
After identity hash: 7bf90a55
0x0000 01 55 0a f9 7b 00 00 00 d8 dc 1b bd fe 00 00 00
on a 32-bit JVM:
-- hashHeader(com.carrotsearch.sizeofexamples.E02_Header)
Clean. [JVM: Java HotSpot(TM) Server VM, 11.0-b16, 1.6.0_11]
0x0000 01 00 00 00 90 5c 3d b2 fe 00 00 00 00 00 00 00
After identity hash: 005e176f
0x0000 81 b7 0b 2f 90 5c 3d b2 fe 00 00 00 00 00 00 00
(2f0bb781 >> 7 = 5e176f)
38. on a 64-bit JVM:
-- hashHeader(com.carrotsearch.sizeofexamples.E02_Header)
Clean. [JVM: Java HotSpot(TM) 64-Bit Server VM, 21.0-b17, 1.7.0]
0x0000 01 00 00 00 00 00 00 00 d8 dc 1b bd fe 00 00 00
After identity hash: 7bf90a55
0x0000 01 55 0a f9 7b 00 00 00 d8 dc 1b bd fe 00 00 00
on a 32-bit JVM:
-- hashHeader(com.carrotsearch.sizeofexamples.E02_Header)
Clean. [JVM: Java HotSpot(TM) Server VM, 11.0-b16, 1.6.0_11]
0x0000 01 00 00 00 90 5c 3d b2 fe 00 00 00 00 00 00 00
After identity hash: 005e176f
0x0000 81 b7 0b 2f 90 5c 3d b2 fe 00 00 00 00 00 00 00
(2f0bb781 >> 7 = 5e176f)
on a 64-bit JVM, full refs (-XX:-UseCompressedOops):
-- hashHeader(com.carrotsearch.sizeofexamples.E02_Header)
Clean. [JVM: Java HotSpot(TM) 64-Bit Server VM, 21.0-b17, 1.7.0]
0x0000 01 00 00 00 00 00 00 00 d0 d6 eb a3 13 7f 00 00
0x0010 fe 00 00 00 00 00 00 00
After identity hash: 7bf90a55
0x0000 01 55 0a f9 7b 00 00 00 d0 d6 eb a3 13 7f 00 00
0x0010 fe 00 00 00 00 00 00 00
39. Conclusions so far
System hashCode is not a full int range (!).
So hash collisions can be more frequent than needed.
Memory consumption may vary
Depending on architecture and settings.
Compact OOPs are an immediate gain.
And they're fast -- we will see why later.
41. Unsafe can provide the "offset" of a eld relative to its base.
public static class MyClass {
public byte byteField = (byte) 0xfe;
}
Object o1 = new MyClass();
System.out.println(BlackMagic.objectMemoryAsString(o1));
Unsafe unsafe = UnsafeAccess.unsafe();
System.out.println(
"byteField offset: " +
unsafe.objectFieldOffset(MyClass.class.getDeclaredField("byteField")));
.
.
E03 Fields .
.
42. Unsafe can provide the "offset" of a eld relative to its base.
public static class MyClass {
public byte byteField = (byte) 0xfe;
}
Object o1 = new MyClass();
System.out.println(BlackMagic.objectMemoryAsString(o1));
Unsafe unsafe = UnsafeAccess.unsafe();
System.out.println(
"byteField offset: " +
unsafe.objectFieldOffset(MyClass.class.getDeclaredField("byteField")));
-- simpleFieldOffset(com.carrotsearch.sizeofexamples.E03_Fields)
0x0000 01 00 00 00 00 00 00 00 82 3c 64 ef fe 00 00 00
byteField offset: 12
.
.
E03 Fields .
.
43. What will happen if we add elds of different types?
public static class MyClass2 {
public byte byteField = (byte) 0xfe;
public short shortField = (short) 0xaabb;
public long longField = 0x1122334455667788L;
}
44. What will happen if we add elds of different types?
public static class MyClass2 {
public byte byteField = (byte) 0xfe;
public short shortField = (short) 0xaabb;
public long longField = 0x1122334455667788L;
}
-- twoFieldOffsets(com.carrotsearch.sizeofexamples.E03_Fields)
0x0000 01 00 00 00 00 00 00 00 59 3a 68 ef bb aa fe 00
0x0010 88 77 66 55 44 33 22 11
byteField offset: 14
shortField offset: 12
longField offset: 16
45. What will happen if we add elds of different types?
public static class MyClass2 {
public byte byteField = (byte) 0xfe;
public short shortField = (short) 0xaabb;
public long longField = 0x1122334455667788L;
}
-- twoFieldOffsets(com.carrotsearch.sizeofexamples.E03_Fields)
0x0000 01 00 00 00 00 00 00 00 59 3a 68 ef bb aa fe 00
0x0010 88 77 66 55 44 33 22 11
byteField offset: 14
shortField offset: 12
longField offset: 16
Note the 'gap' at offset 15 -- longs are aligned (in this JVM).
46. What will happen if we add elds of different types?
public static class MyClass2 {
public byte byteField = (byte) 0xfe;
public short shortField = (short) 0xaabb;
public long longField = 0x1122334455667788L;
}
-- twoFieldOffsets(com.carrotsearch.sizeofexamples.E03_Fields)
0x0000 01 00 00 00 00 00 00 00 59 3a 68 ef bb aa fe 00
0x0010 88 77 66 55 44 33 22 11
byteField offset: 14
shortField offset: 12
longField offset: 16
Note the 'gap' at offset 15 -- longs are aligned (in this JVM).
Think of what the gap would be if we just had a long eld?
47. What happens if we have inheritance? Say:
public static class MyClass2 {
public byte byteField = (byte) 0xfe;
public short shortField = (short) 0xaabb;
public long longField = 0x1122334455667788L;
}
public static class MyClass3 extends MyClass2 {
public byte byteField2 = (byte) 0xfa;
}
48. What happens if we have inheritance? Say:
public static class MyClass2 {
public byte byteField = (byte) 0xfe;
public short shortField = (short) 0xaabb;
public long longField = 0x1122334455667788L;
}
public static class MyClass3 extends MyClass2 {
public byte byteField2 = (byte) 0xfa;
}
-- inheritedFields(com.carrotsearch.sizeofexamples.E03_Fields)
0x0000 01 00 00 00 00 00 00 00 3d 8f 67 ef bb aa fe 00
0x0010 88 77 66 55 44 33 22 11 ff 00 00 00 00 00 00 00
byteField2 offset: 24
byteField offset: 14
shortField offset: 12
longField offset: 16
49. What happens if we have inheritance? Say:
public static class MyClass2 {
public byte byteField = (byte) 0xfe;
public short shortField = (short) 0xaabb;
public long longField = 0x1122334455667788L;
}
public static class MyClass3 extends MyClass2 {
public byte byteField2 = (byte) 0xfa;
}
-- inheritedFields(com.carrotsearch.sizeofexamples.E03_Fields)
0x0000 01 00 00 00 00 00 00 00 3d 8f 67 ef bb aa fe 00
0x0010 88 77 66 55 44 33 22 11 ff 00 00 00 00 00 00 00
byteField2 offset: 24
byteField offset: 14
shortField offset: 12
longField offset: 16
But: IBM's J9 can "pack" elds in an inheritance hierarchy.
50. How about different VMs?
public static class MyClass4 {
public byte byteField1 = (byte) 0x01;
public byte byteField2 = (byte) 0x02;
public byte byteField3 = (byte) 0x03;
public byte byteField4 = (byte) 0x04;
public short shortField = (short) 0xa1a2;
public long longField = 0xb1b2b3b4b5b6b7b8L;
}
51. How about different VMs?
public static class MyClass4 {
public byte byteField1 = (byte) 0x01;
public byte byteField2 = (byte) 0x02;
public byte byteField3 = (byte) 0x03;
public byte byteField4 = (byte) 0x04;
public short shortField = (short) 0xa1a2;
public long longField = 0xb1b2b3b4b5b6b7b8L;
}
-- vmVariance(com.carrotsearch.sizeofexamples.E03_Fields)
[JVM: Oracle JRockit(R), R28.2.3-13-149708-1.6.0_31-20120327-1523-windows-x86_64]
0x0000 e0 2f 47 02 00 00 00 00 b8 b7 b6 b5 b4 b3 b2 b1
0x0010 a2 a1 01 02 03 04 00 00
52. How about different VMs?
public static class MyClass4 {
public byte byteField1 = (byte) 0x01;
public byte byteField2 = (byte) 0x02;
public byte byteField3 = (byte) 0x03;
public byte byteField4 = (byte) 0x04;
public short shortField = (short) 0xa1a2;
public long longField = 0xb1b2b3b4b5b6b7b8L;
}
-- vmVariance(com.carrotsearch.sizeofexamples.E03_Fields)
[JVM: Oracle JRockit(R), R28.2.3-13-149708-1.6.0_31-20120327-1523-windows-x86_64]
0x0000 e0 2f 47 02 00 00 00 00 b8 b7 b6 b5 b4 b3 b2 b1
0x0010 a2 a1 01 02 03 04 00 00
-- vmVariance(com.carrotsearch.sizeofexamples.E03_Fields)
[JVM: Java HotSpot(TM) 64-Bit Server VM, 22.1-b02, Oracle Corporation, Oracle Corporation, 1.
0x0000 01 00 00 00 00 00 00 00 44 90 67 ef a2 a1 01 02
0x0010 b8 b7 b6 b5 b4 b3 b2 b1 03 04 00 00 00 00 00 00
53. How about different VMs?
public static class MyClass4 {
public byte byteField1 = (byte) 0x01;
public byte byteField2 = (byte) 0x02;
public byte byteField3 = (byte) 0x03;
public byte byteField4 = (byte) 0x04;
public short shortField = (short) 0xa1a2;
public long longField = 0xb1b2b3b4b5b6b7b8L;
}
-- vmVariance(com.carrotsearch.sizeofexamples.E03_Fields)
[JVM: Oracle JRockit(R), R28.2.3-13-149708-1.6.0_31-20120327-1523-windows-x86_64]
0x0000 e0 2f 47 02 00 00 00 00 b8 b7 b6 b5 b4 b3 b2 b1
0x0010 a2 a1 01 02 03 04 00 00
-- vmVariance(com.carrotsearch.sizeofexamples.E03_Fields)
[JVM: Java HotSpot(TM) 64-Bit Server VM, 22.1-b02, Oracle Corporation, Oracle Corporation, 1.
0x0000 01 00 00 00 00 00 00 00 44 90 67 ef a2 a1 01 02
0x0010 b8 b7 b6 b5 b4 b3 b2 b1 03 04 00 00 00 00 00 00
-- vmVariance(com.carrotsearch.sizeofexamples.E03_Fields)
[JVM: IBM J9 VM, 2.4, IBM Corporation, IBM Corporation, 1.6.0]
0x0000 b0 6a 7f 6b 0e 80 a6 43 00 00 00 00 b8 b7 b6 b5
0x0010 b4 b3 b2 b1 01 00 00 00 02 00 00 00 03 00 00 00
0x0020 04 00 00 00 a2 a1 ff ff
(note byte elds are also padded on J9!).
54. Conclusions so far
Field order and layout varies greatly
Each VM/hardware con g will have its own.
Memory is "lost" on paddings
VMs try to reorder and pack elds though.
Paddings are not useless crap
VM designers are machine code experts!
56. How large is an object of MyClass (byte#)?
public static class MyClass {
public byte byteField = (byte) 0xfe;
}
57. How large is an object of MyClass (byte#)?
public static class MyClass {
public byte byteField = (byte) 0xfe;
}
-- simpleFieldOffset(com.carrotsearch.sizeofexamples.E03_Fields)
0x0000 01 00 00 00 00 00 00 00 82 3c 64 ef fe 00 00 00
byteField offset: 12
58. How large is an object of MyClass (byte#)?
public static class MyClass {
public byte byteField = (byte) 0xfe;
}
-- simpleFieldOffset(com.carrotsearch.sizeofexamples.E03_Fields)
0x0000 01 00 00 00 00 00 00 00 82 3c 64 ef fe 00 00 00
byteField offset: 12
Objects are placed on a "grid" of aligned addresses.
59. How large is an object of MyClass (byte#)?
public static class MyClass {
public byte byteField = (byte) 0xfe;
}
-- simpleFieldOffset(com.carrotsearch.sizeofexamples.E03_Fields)
0x0000 01 00 00 00 00 00 00 00 82 3c 64 ef fe 00 00 00
byteField offset: 12
Objects are placed on a "grid" of aligned addresses.
Efficient compact references: address scaling by a constant.
60. How can we determine the size of object padding in use?
.
E04 Paddings .
.
.
61. How can we determine the size of object padding in use?
On HotSpot:
com.sun.management.HotSpotDiagnosticMXBean mbean =
(HotSpotDiagnosticMXBean) ManagementFactory.newPlatformMXBeanProxy(
ManagementFactory.getPlatformMBeanServer(),
"com.sun.management:type=HotSpotDiagnostic",
HotSpotDiagnosticMXBean.class);
System.out.println("ObjectAlignment: " +
mbean.getVMOption("ObjectAlignmentInBytes").getValue());
System.out.println(BlackMagic.objectMemoryAsString(new Object()));
.
E04 Paddings .
.
.
62. How can we determine the size of object padding in use?
On HotSpot:
com.sun.management.HotSpotDiagnosticMXBean mbean =
(HotSpotDiagnosticMXBean) ManagementFactory.newPlatformMXBeanProxy(
ManagementFactory.getPlatformMBeanServer(),
"com.sun.management:type=HotSpotDiagnostic",
HotSpotDiagnosticMXBean.class);
System.out.println("ObjectAlignment: " +
mbean.getVMOption("ObjectAlignmentInBytes").getValue());
System.out.println(BlackMagic.objectMemoryAsString(new Object()));
-- simpleFieldOffset(com.carrotsearch.sizeofexamples.E04_Paddings)
ObjectAlignment: 8
0x0000 01 00 00 00 00 00 00 00 cb 11 5c ef 00 00 00 00
.
E04 Paddings .
.
.
63. Wait. . . if it's an option, can we change it?!
-XX:ObjectAlignmentInBytes=32
67. Arrays
The same kind of analysis can be applied to arrays:
• where is array's length stored?
• how are individual elements stored?
• are there any paddings?
68. Arrays
The same kind of analysis can be applied to arrays:
• where is array's length stored?
• how are individual elements stored?
• are there any paddings?
This is your homework :)
70. False sharing
Memory split into cache lines
64 bytes, typically. With multiple cache levels.
Cache access speeds vary
A lot!
Fields or even objects can share a cache line.
Multithreaded access to con icting cache lines will slow down apps.
71. An example (code simpli ed):
AtomicLongArray array = new AtomicLongArray(largeEnough);
for (int threads = 1; threads <= 8; threads++)
for (int sepIndexes : new int [] {0, 0, 0, 1, .... 8, 9, 10, 11, 12, 13, 1000})
runRound(threads, sepIndexes);
runRound(N, sep) {
// start N threads, each reading/ writing to
// array[threads_index * sep]
}
.
E05 FalseSharing .
.
.
73. Conclusions so far
False sharing is a real problem (?)
If you have lots of concurrent operations.
No low-level control in Java (without JNI)
Thread-core affinity, cache line alignments.
Array traversals may be tricky!
Built-in bounds checking may cause false sharing (unless eliminated).
80. java-sizeof
https://github.com/dweiss/java-sizeof
Ready-to-use code, including object hexdumper.
Similar code in Apache Lucene (joint work with Uwe Schindler).