Skip to content

Commit 855f459

Browse files
committed
Add Object Header Size in Native Image docs
1 parent 30492c3 commit 855f459

File tree

2 files changed

+87
-0
lines changed

2 files changed

+87
-0
lines changed
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
---
2+
layout: docs
3+
toc_group: optimizations-and-performance
4+
link_title: Object Header Size in Native Image
5+
permalink: /reference-manual/native-image/optimizations-and-performance/ObjectHeaderSize/
6+
---
7+
8+
# Object Header Size in Native Image
9+
10+
The object header is part of every object in memory, storing metadata about the object, and its size varies depending on the JVM implementation, and specific JVM options such as compressed references.
11+
The size of the object header directly affects the memory footprint of a Java application, particularly if a lot of small objects are allocated.
12+
13+
In Oracle GraalVM Native Image, the object header is 4 bytes by default, which is smaller than when running on HotSpot.
14+
15+
For example, in a 64-bit HotSpot VM with compressed references, an instance of `java.lang.Object` consumes 16 bytes (12-byte header plus 4-byte padding).
16+
Using Oracle GraalVM Native Image, the same object consumes only 8 bytes, offering significant memory savings.
17+
However, in case of Native Image, the object size heavily depends on the used garbage collector (GC), the allocated instance type, and the state of compressed references.
18+
Compressed references use 32-bit instead of 64-bit, and are enabled by default in Oracle GraalVM.
19+
20+
To observe the memory usage differences, consider this example application that measures thread-allocated bytes using the [ThreadMXBean API](https://docs.oracle.com/en/java/javase/21/docs/api/java.management/java/lang/management/ThreadMXBean.html):
21+
```java
22+
import com.sun.management.ThreadMXBean;
23+
import java.lang.management.ManagementFactory;
24+
import java.util.ArrayList;
25+
26+
public class ObjectSize {
27+
public static void main(String[] args) {
28+
long threadId = Thread.currentThread().threadId();
29+
ThreadMXBean threadMXBean = (com.sun.management.ThreadMXBean) ManagementFactory.getThreadMXBean();
30+
long initialValue = threadMXBean.getThreadAllocatedBytes(threadId);
31+
32+
int count = 12 * 1024 * 1024;
33+
ArrayList<Object> objects = new ArrayList<>(count);
34+
for (int i = 0; i < count; i++) {
35+
objects.add(new Object());
36+
}
37+
38+
long allocatedBytes = threadMXBean.getThreadAllocatedBytes(threadId) - initialValue;
39+
System.out.println("Object allocation test completed: " + objects.hashCode());
40+
System.out.println("Thread allocated " + allocatedBytes + " bytes");
41+
}
42+
}
43+
```
44+
45+
The application creates millions of object instances and calculates the total memory allocated during their creation.
46+
The application reports the total allocated bytes, which include both the memory for `ArrayList` and the individual objects.
47+
48+
Running this application on a machine with 16 GB of RAM and **Oracle GraalVM for JDK 23**, produces the following results.
49+
50+
#### Native Image with compressed references and default Serial GC:
51+
```
52+
Object allocation test completed: -718496536
53+
Thread allocated 150995032 bytes
54+
```
55+
Breaking this down translates to:
56+
```
57+
48 MB for the ArrayList
58+
96 MB for the Objects (12 * 1024 * 1024 objects × 8 bytes)
59+
----------------------------------------------------------
60+
Total: 144 MB
61+
```
62+
63+
#### HotSpot with compressed references and default G1 GC:
64+
```
65+
Object allocation test completed: -1131298887
66+
Thread allocated 251658592 bytes
67+
```
68+
69+
Breaking this down translates to:
70+
```
71+
48 MB for the ArrayList
72+
192 MB for the Objects (12 * 1024 * 1024 objects × 16 bytes)
73+
------------------------------------------------------------
74+
Total: 240 MB
75+
```
76+
77+
The primary difference lies in the object header size (4-byte header vs 12-byte header).
78+
Note that the memory footprint for the `ArrayList` is roughly identical in both VMs.
79+
However, the memory for the millions of individual objects diverges due to the larger object headers on HotSpot.
80+
81+
To summarize, when it comes to applications dealing with large numbers of small objects, Native Image may offer a smaller memory footprint.
82+
For Native Image, the object header size primarily depends on the used GC, the allocated instance type, and the state of compressed references.
83+
84+
### Further Reading
85+
86+
- [Memory Management](MemoryManagement.md)

docs/reference-manual/native-image/OptimizationsAndPerformance.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,6 @@ This reduces the set of instructions used by the compiler to a minimum and thus
5050

5151
Native Image provides additional features to further optimize a generated binary:
5252
- Choosing an appropriate Garbage Collector and tailoring the garbage collection policy can reduce GC times. See [Memory Management](MemoryManagement.md).
53+
- Using compressed references can lead to better memory efficiency. See [Object Header Size in Native Image](ObjectHeaderSize.md).
5354
- Loading application configuration during the image build can speed up application startup. See [Class Initialization at Image Build Time](ClassInitialization.md).
5455
- The build output may provide some other recommendations that help you get the best out of Native Image. See [Build Output: Recommendations](BuildOutput.md#recommendations).

0 commit comments

Comments
 (0)