Author: threedayman
Source: Hang Seng LIGHT Cloud Community
The following content is explained and verified based on the HotSpot VIRTUAL machine.
The theoretical knowledge
The storage layout of objects in heap memory can be divided into three parts: object headers, Instance Data, and Padding.
There are two types of information in the object header
- Runtime data: hash code, GC generation age, lock status flags, locks held by threads, bias thread ID, bias timestamp, etc.
- Type pointer: A pointer to the object’s type metadata that the virtual machine uses to determine which class the object is an instance of.
The instance data
- Objects store valid information, that is, the contents of various types of fields that we define in our program code.
Alignment filling
- The HotSpot VIRTUAL machine’s automatic memory management system requires that the object’s starting address be an integer multiple of 8 bytes. If the object is not an integer multiple of 8 bytes, it needs to be filled by alignment.
The size of the object footprint is affected by the JVM parameter UseCompressedOops. For 64-bit operating systems, the object header size is 12bytes when enabled and the compression pointer is 16bytes when turned off.
The practice test
Through Instrumentation getObjectSize method to measure the size of the object occupied. The specific code is as follows
package org.example; import java.lang.instrument.Instrumentation; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayDeque; import java.util.Deque; import java.util.HashSet; import java.util.Set; public class SizeOfObject { static Instrumentation inst; public static void premain(String args, Instrumentation instP) { inst = instP; } /** * directly calculate the current size of the object, including the current class and superclass primitive type instance field size, <br></br> * reference type instance field reference size, instance primitive type array total space occupied, instance reference type array reference size itself occupied space; <br></br> * Public static long <br></br> * * @param obj * @return */ public static long <br></br> * * @param obj * @return */ sizeOf(Object obj) { return inst.getObjectSize(obj); } /** * Recursively calculate the total space occupied by the current object, Includes the instance field size of the current class and superclass and the object size of the instance field reference * * @param objP * @return * @throws IllegalAccessException */ public static Long fullSizeOf(Object objP) throws IllegalAccessException { Set<Object> visited = new HashSet<Object>(); Deque<Object> toBeQueue = new ArrayDeque<>(); toBeQueue.add(objP); long size = 0L; while (toBeQueue.size() > 0) { Object obj = toBeQueue.poll(); // The size of the base type and the length of the reference are taken into account, including the array size += skipObject(visited, obj)? 0L : sizeOf(obj); Class<? > tmpObjClass = obj.getClass(); If (tmPObjclass.isarray ()) {// if (tmPobjclass.getName ().length() > 2) {for (int I = 0, len = Array.getLength(obj); i < len; i++) { Object tmp = Array.get(obj, i); if (tmp ! Add (array.get (obj, I));}}}} else {while (tmpObjClass! = null) { Field[] fields = tmpObjClass.getDeclaredFields(); for (Field field : Fields) {if (Modifier. IsStatic (field. GetModifiers ()) / / static regardless of the | | field. The getType () isPrimitive ()) {/ / basic types do not count the continue repeating; } field.setAccessible(true); Object fieldValue = field.get(obj); if (fieldValue == null) { continue; } toBeQueue.add(fieldValue); } tmpObjClass = tmpObjClass.getSuperclass(); } } } return size; } /** * String. Intern objects do not count; The calculated ones don't count, * * @param visited * @param obj * @return */ static Boolean skipObject(Set<Object> visited, Object obj) { if (obj instanceof String && obj == ((String) obj).intern()) { return true; } return visited.contains(obj); } }Copy the code
Test the object size code
package org.example; import static org.example.SizeOfObject.fullSizeOf; import static org.example.SizeOfObject.sizeOf; public class SizeOfObjectTest { /** * -XX:+UseCompressedOops: Header 12 Padding 4= 16 * -XX:-UseCompressedOops: Header 16 = 24 */ static class A1 { } /** * -XX:+UseCompressedOops: Header 12 + Instance Data 4 = 16 * -XX:-UseCompressedOops: Header 16 + Instance Data 4 + Padding 4 = 24 */ static class A { int a; } /** * -XX:+UseCompressedOops: Header 12 + Instance Data 4 + 4 + padding 4 = 24 * -XX:-UseCompressedOops: Header 16 + Instance Data 4 + 4 = 24 */ static class B { int a; int b; } /** * -XX:+UseCompressedOops: Header 12 + 4 + 4 + padding/4 = 24 * -XX:-UseCompressedOops:Header 16 + 8 + 4 + padding/4 = 32 */ static class B2 { int b2a; Integer b2b; } static class C extends A {int ba;} static class C extends A {int ba; B[] as = new B[3]; C() { for (int i = 0; i < as.length; i++) { as[i] = new B(); } } } static class D extends B { int da; Integer[] di = new Integer[3]; } static class E extends A {int ea; int eb; } public static void main(String[] args) throws IllegalAccessException { System.out.println("sizeOf(new A1())=" + sizeOf(new A1())); System.out.println("sizeOf(new A())=" + sizeOf(new A())); System.out.println("sizeOf(new B())=" + sizeOf(new B())); System.out.println("sizeOf(new B2())=" + sizeOf(new B2())); System.out.println("sizeOf(new B[3])=" + sizeOf(new B[3])); System.out.println("sizeOf(new C())=" + sizeOf(new C())); System.out.println("fullSizeOf(new C())=" + fullSizeOf(new C())); System.out.println("sizeOf(new D())=" + sizeOf(new D())); System.out.println("fullSizeOf(new D())=" + fullSizeOf(new D())); System.out.println("sizeOf(new int[3])=" + sizeOf(new int[3])); System.out.println("sizeOf(new Integer(1)=" + sizeOf(new Integer(1))); System.out.println("sizeOf(new Integer[0])=" + sizeOf(new Integer[0])); System.out.println("sizeOf(new Integer[1])=" + sizeOf(new Integer[1])); System.out.println("sizeOf(new Integer[2])=" + sizeOf(new Integer[2])); System.out.println("sizeOf(new Integer[3])=" + sizeOf(new Integer[3])); System.out.println("sizeOf(new Integer[4])=" + sizeOf(new Integer[4])); System.out.println("sizeOf(new A[3])=" + sizeOf(new A[3])); System.out.println("sizeOf(new E())=" + sizeOf(new E())); }}Copy the code
Add the following configuration to the package plug-in
<plugin> <artifactId> Maven-jar-plugin </artifactId> <version>3.0.2</version> <configuration> <finalName>test</finalName> <archive> <manifestEntries> <Premain-class>org.example.SizeOfObject</Premain-class> <Boot-Class-Path></Boot-Class-Path> <Can-Redefine-Classes>false</Can-Redefine-Classes> <Main-Class>org.example.SizeOfObjectTest</Main-Class> </manifestEntries> <addMavenDescriptor>false</addMavenDescriptor> </archive> </configuration> </plugin>Copy the code
Configure the specified main method entry class and Agent enhancement class entry. Pointer compression is turned on by default.
D:\ideaproject\size-agent\target>java -javaagent:test.jar -jar test.jar sizeOf(new A1())=16 sizeOf(new A())=16 sizeOf(new B())=24 sizeOf(new B2())=24 sizeOf(new B[3])=32 sizeOf(new C())=24 fullSizeOf(new C())=128 sizeOf(new D())=32 fullSizeOf(new D())=64 sizeOf(new int[3])=32 sizeOf(new Integer(1)=16 sizeOf(new Integer[0])=16 sizeOf(new Integer[1])=24 sizeOf(new Integer[2])=24 sizeOf(new Integer[3])=32 sizeOf(new Integer[4])=32 sizeOf(new A[3])=32 sizeOf(new E())=24Copy the code
Turn off pointer compression
D:\ideaproject\size-agent\target>java -javaagent:test.jar -XX:-UseCompressedOops -jar test.jar sizeOf(new A1())=16 sizeOf(new A())=24 sizeOf(new B())=24 sizeOf(new B2())=32 sizeOf(new B[3])=48 sizeOf(new C())=40 fullSizeOf(new C())=160 sizeOf(new D())=40 fullSizeOf(new D())=88 sizeOf(new int[3])=40 sizeOf(new Integer(1)=24 sizeOf(new Integer[0])=24 sizeOf(new Integer[1])=32 sizeOf(new Integer[2])=40 sizeOf(new Integer[3])=48 sizeOf(new Integer[4])=56 sizeOf(new A[3])=48 sizeOf(new E())=32Copy the code
You can also verify your theory by changing the code as you wish.
reference
In-depth Understanding of the Java Virtual Machine
www.iteye.com/blog/yueyem…