In this tutorial you will learn how you can use the JOL (Java Object Layout) toolbox to monitor the size of Java Objects.
JOL is tiny toolbox available in OpenJDK to analyze Java object layout schemes. This tool uses Unsafe, JVMTI, and Serviceability Agent (SA) heavily to decoder the actual object layout, footprint, and references. This makes JOL much more accurate than other tools relying on heap dumps, specification assumptions, etc.
You can use JOL in different ways, as a Command Line Tool or using directly its API in your code.
Using JOL as Command Line Utility
As command line utility, you will have at first to clone the project available at: http://hg.openjdk.java.net/code-tools/jol/ Then build JOL with Maven:
$ cd jol/ $ mvn clean install
Now you can run the executable JAR file jol-cli.jar to get:
- Object internals, that is instances of that object
- Object externals, that is, the objects reachable from the instance
$ java -jar jol-cli/target/jol-cli.jar internals java.util.HashMap Running 64-bit HotSpot VM. Using compressed oop with 3-bit shift. Using compressed klass with 3-bit shift. Objects are 8 bytes aligned. Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes] Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes] java.util.HashMap object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 00 00 00 (0000 0101 0000 0000 0000 0000 0000 0000) 4 4 (object header) 00 00 00 00 (0000 0000 0000 0000 0000 0000 0000 0000) 8 4 (object header) 8c 3b 00 f8 (1000 1100 0011 1011 0000 0000 1111 1000) 12 4 Set AbstractMap.keySet null 16 4 Collection AbstractMap.values null 20 4 int HashMap.size 0 24 4 int HashMap.modCount 0 28 4 int HashMap.threshold 0 32 4 float HashMap.loadFactor 0.75 36 4 Node[] HashMap.table null 40 4 Set HashMap.entrySet null 44 4 (loss due to the next object alignment) Instance size: 48 bytes (reported by Instrumentation API) Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
Calculate objects size from your code
if you need to calculate at runtime the size of your objects, then you need at first to include its dependency so that you can build your project:
<dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>0.8</version> </dependency>
Once done, you will be able to access the org.openjdk.jol package as in the following example class:
package com.mastertheboss; import java.util.ArrayList; import java.util.List; import org.openjdk.jol.info.ClassLayout; import org.openjdk.jol.info.GraphLayout; public class MemoryTest { @SuppressWarnings("restriction") public static void main(String[] args) { int size = 10; List<Integer> list = new ArrayList<Integer>(size); for (int i = 0; i < size; i++) { list.add(i); } System.out.println(ClassLayout.parseClass(ArrayList.class).toPrintable()); System.out.println(ClassLayout.parseClass(ArrayList.class).toPrintable(list)); System.out.println(GraphLayout.parseInstance(list).toPrintable()); System.out.println(GraphLayout.parseInstance(list).toFootprint()); } }
The first method, that is:
System.out.println(ClassLayout.parseClass(ArrayList.class).toPrintable());
Produces a printable stringly representation of class layout. This method does not require alive instance, just the class.
java.util.ArrayList object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 12 (object header) N/A 12 4 int AbstractList.modCount N/A 16 4 int ArrayList.size N/A 20 4 java.lang.Object[] ArrayList.elementData N/A Instance size: 24 bytes Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
On the other hand, next one:
System.out.println(ClassLayout.parseClass(ArrayList.class).toPrintable(list));
Produces a String representation of a live class instance with its objects:
java.util.ArrayList object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) 9e 36 00 f8 (10011110 00110110 00000000 11111000) (-134203746) 12 4 int AbstractList.modCount 10 16 4 int ArrayList.size 10 20 4 java.lang.Object[] ArrayList.elementData [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Instance size: 24 bytes Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
In order to produce a representation of graph of external objects, we’ll use the following method:
System.out.println(GraphLayout.parseInstance(list).toPrintable());
Which produces this output for our ArrayList:
java.util.ArrayList@368102c8d object externals: ADDRESS SIZE TYPE PATH VALUE 782faf2d0 16 java.lang.Integer .elementData[0] 0 782faf2e0 16 java.lang.Integer .elementData[1] 1 782faf2f0 16 java.lang.Integer .elementData[2] 2 782faf300 16 java.lang.Integer .elementData[3] 3 782faf310 16 java.lang.Integer .elementData[4] 4 782faf320 16 java.lang.Integer .elementData[5] 5 782faf330 16 java.lang.Integer .elementData[6] 6 782faf340 16 java.lang.Integer .elementData[7] 7 782faf350 16 java.lang.Integer .elementData[8] 8 782faf360 16 java.lang.Integer .elementData[9] 9 782faf370 246184 (something else) (somewhere else) (something else) 782feb518 24 java.util.ArrayList (object) 782feb530 56 [Ljava.lang.Object; .elementData [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
And finally, in order to calculate the memory footprint of the ArrayList:
System.out.println(GraphLayout.parseInstance(list).toFootprint());
Here’s the output:
java.util.ArrayList@368102c8d footprint: COUNT AVG SUM DESCRIPTION 1 56 56 [Ljava.lang.Object; 10 16 160 java.lang.Integer 1 24 24 java.util.ArrayList 12 240 (total)