1. Java 对象的内存布局
Java的实例对象、数组对象在内存中的组成包括如下三部分:对象头Hearder、实例数据、内存填充。示意图如下所示
-
对象头
其主要包括两部分数据:Mark Word、Class对象指针。特别地对于数组对象而言,其还包括了数组长度数据。在64位的HotSpot虚拟机下,Mark Word占8个字节,其记录了Hash Code、GC信息、锁信息等相关信息;而Class对象指针则指向该实例的Class对象,在开启指针压缩的情况下占用4个字节,否则占8个字节;如果其是一个数组对象,则还需要4个字节用于记录数组长度信息。这里列出64位HotSpot虚拟机Mark Word的具体含义,以供参考。需要注意的是在下图的Mark Word中,左侧为高字节,右侧为低字节
-
实例数据
用于存放该对象的实例数据 -
内存填充
64位的HotSpot要求Java对象地址按8字节对齐,即每个对象所占内存的字节数必须是8字节的整数倍。因此Java对象需要通过内存填充来满足对齐要求
注:
在64位的HotSpot虚拟机下,类型指针、引用类型需要占8个字节。显然这大大增加了内存的消耗和占用。为此从JDK 1.6开始,64位的JVM支持UseCompressedOops选项。其可对OOP(Ordinary Object Pointer,普通对象指针)进行压缩,使其只占用4个字节,以达到节约内存的目的。在JDK 8下,该选项默认启用。当然也可以通过添加JVM参数来显式进行配置
-XX:+UseCompressedOops // 开启指针压缩
-XX:-UseCompressedOops // 关闭指针压缩
2. 代码示例获取Java对象的大小
引入maven依赖
<dependency><groupId>org.openjdk.jol</groupId><artifactId>jol-core</artifactId><version>0.17</version><scope>compile</scope>
</dependency>
2.1 基本类型的大小
package com.zishi.jvm.jol.ab;
public class A {boolean bo1;boolean bo2;byte b1;byte b2;char c1;char c2;double d1;double d2;float f1;float f2;int i1;int i2;long l1;long l2;short s1;short s2;
}
public static void main(String[] args) {System.out.println(ClassLayout.parseClass(A.class).toPrintable());
}
关闭压缩的结果如下:
com.zishi.jvm.jol.ab.A object internals:
OFF SZ TYPE DESCRIPTION VALUE0 8 (object header: mark) N/A8 8 (object header: class) N/A -- 这里占了8个字节16 8 double A.d1 N/A24 8 double A.d2 N/A32 8 long A.l1 N/A40 8 long A.l2 N/A48 4 float A.f1 N/A52 4 float A.f2 N/A56 4 int A.i1 N/A60 4 int A.i2 N/A64 2 char A.c1 N/A66 2 char A.c2 N/A68 2 short A.s1 N/A70 2 short A.s2 N/A72 1 boolean A.bo1 N/A73 1 boolean A.bo2 N/A74 1 byte A.b1 N/A75 1 byte A.b2 N/A76 4 (object alignment gap) -- 对齐了4个字节
Instance size: 80 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
开启压缩的结果如下:
com.zishi.jvm.jol.ab.A object internals:
OFF SZ TYPE DESCRIPTION VALUE0 8 (object header: mark) N/A8 4 (object header: class) N/A -- 这里占了4个字节12 4 float A.f1 N/A16 8 double A.d1 N/A24 8 double A.d2 N/A32 8 long A.l1 N/A40 8 long A.l2 N/A48 4 float A.f2 N/A52 4 int A.i1 N/A56 4 int A.i2 N/A60 2 char A.c1 N/A62 2 char A.c2 N/A64 2 short A.s1 N/A66 2 short A.s2 N/A68 1 boolean A.bo1 N/A69 1 boolean A.bo2 N/A70 1 byte A.b1 N/A71 1 byte A.b2 N/A
Instance size: 72 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
注:以下代码均开启压缩
2.2 包装类型的大小
public class B {Boolean bo1;Byte b1;Character c1;Double d1;Float f1;Integer i1;Long l1;Short s1;
}
结果如下:
com.zishi.jvm.jol.ab.B object internals:
OFF SZ TYPE DESCRIPTION VALUE0 8 (object header: mark) N/A8 4 (object header: class) N/A12 4 java.lang.Boolean B.bo1 N/A16 4 java.lang.Byte B.b1 N/A20 4 java.lang.Character B.c1 N/A24 4 java.lang.Double B.d1 N/A28 4 java.lang.Float B.f1 N/A32 4 java.lang.Integer B.i1 N/A36 4 java.lang.Long B.l1 N/A40 4 java.lang.Short B.s1 N/A44 4 (object alignment gap)
Instance size: 48 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
3.3 数组对象的大小(int[] 为例说明)
public static void main(String[] args) {int[] array = new int[3];array[0] = 11;array[1] = 22;array[2] = 33;System.out.println( ClassLayout.parseInstance(array).toPrintable() );}
结果如下:
[I object internals:
OFF SZ TYPE DESCRIPTION VALUE0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0) --对象头8 4 (object header: class) 0x00000c10 -- 对象字节头信息12 4 (array length) 3 --数组的长度16 12 int [I.<elements> N/A -- 数组元素占的空间28 4 (object alignment gap) -- 对齐
Instance size: 32 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
数组的对象头里面包含了数组的长度,占4个字节。
3.3 继承
public class GrandFather {private int age;protected double money;public double bbb;
}
public class Son extends Father {private double rrr;
}
public class Father extends GrandFather {protected double money;protected double ddd;
}
main方法:
public static void main(String[] args) {System.out.println(ClassLayout.parseClass(GrandFather.class).toPrintable());System.out.println("-----------------------------------------------------------------");System.out.println(ClassLayout.parseClass(Father.class).toPrintable());System.out.println("-----------------------------------------------------------------");System.out.println(ClassLayout.parseClass(Son.class).toPrintable());
}
com.zishi.jvm.jol.ab.GrandFather object internals:
OFF SZ TYPE DESCRIPTION VALUE0 8 (object header: mark) N/A8 4 (object header: class) N/A12 4 int GrandFather.age N/A16 8 double GrandFather.money N/A24 8 double GrandFather.bbb N/A
Instance size: 32 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total-----------------------------------------------------------------
com.zishi.jvm.jol.ab.Father object internals:
OFF SZ TYPE DESCRIPTION VALUE0 8 (object header: mark) N/A8 4 (object header: class) N/A12 4 int GrandFather.age N/A16 8 double GrandFather.money N/A24 8 double GrandFather.bbb N/A32 8 double Father.money N/A40 8 double Father.ddd N/A
Instance size: 48 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total-----------------------------------------------------------------
com.zishi.jvm.jol.ab.Son object internals:
OFF SZ TYPE DESCRIPTION VALUE0 8 (object header: mark) N/A8 4 (object header: class) N/A12 4 int GrandFather.age N/A16 8 double GrandFather.money N/A24 8 double GrandFather.bbb N/A32 8 double Father.money N/A40 8 double Father.ddd N/A48 8 double Son.rrr N/A
Instance size: 56 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
3.4 异常
public static void main(String[] args) {System.out.println(ClassLayout.parseClass(Throwable.class).toPrintable());}
结果如下:
java.lang.Throwable object internals:
OFF SZ TYPE DESCRIPTION VALUE0 8 (object header: mark) N/A8 4 (object header: class) N/A12 4 java.lang.Object Throwable.backtrace N/A16 4 java.lang.String Throwable.detailMessage N/A20 4 java.lang.Throwable Throwable.cause N/A24 4 java.lang.StackTraceElement[] Throwable.stackTrace N/A28 4 java.util.List Throwable.suppressedExceptions N/A32 4 int Throwable.depth N/A36 4 (object alignment gap)
Instance size: 40 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
3.5 Class
public static void main(String[] args) {System.out.println(ClassLayout.parseClass(Class.class).toPrintable());
}
结果如下:
java.lang.Class object internals:
OFF SZ TYPE DESCRIPTION VALUE0 8 (object header: mark) N/A8 4 (object header: class) N/A12 4 java.lang.reflect.Constructor Class.cachedConstructor N/A16 4 java.lang.Class Class.newInstanceCallerCache N/A20 4 java.lang.String Class.name N/A24 4 java.lang.Module Class.module N/A28 4 (alignment/padding gap) 32 4 java.lang.String Class.packageName N/A36 4 java.lang.Class Class.componentType N/A40 4 java.lang.ref.SoftReference Class.reflectionData N/A44 4 sun.reflect.generics.repository.ClassRepository Class.genericInfo N/A48 4 java.lang.Object[] Class.enumConstants N/A52 4 java.util.Map Class.enumConstantDirectory N/A56 4 java.lang.Class.AnnotationData Class.annotationData N/A60 4 sun.reflect.annotation.AnnotationType Class.annotationType N/A64 4 java.lang.ClassValue.ClassValueMap Class.classValueMap N/A68 28 (alignment/padding gap) 96 4 int Class.classRedefinedCount N/A
100 4 (object alignment gap)
Instance size: 104 bytes
Space losses: 32 bytes internal + 4 bytes external = 36 bytes total