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 VALUE
0 8 (object header: mark) N/A
8 8 (object header: class) N/A -- 这里占了8个字节
16 8 double A.d1 N/A
24 8 double A.d2 N/A
32 8 long A.l1 N/A
40 8 long A.l2 N/A
48 4 float A.f1 N/A
52 4 float A.f2 N/A
56 4 int A.i1 N/A
60 4 int A.i2 N/A
64 2 char A.c1 N/A
66 2 char A.c2 N/A
68 2 short A.s1 N/A
70 2 short A.s2 N/A
72 1 boolean A.bo1 N/A
73 1 boolean A.bo2 N/A
74 1 byte A.b1 N/A
75 1 byte A.b2 N/A
76 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 VALUE
0 8 (object header: mark) N/A
8 4 (object header: class) N/A -- 这里占了4个字节
12 4 float A.f1 N/A
16 8 double A.d1 N/A
24 8 double A.d2 N/A
32 8 long A.l1 N/A
40 8 long A.l2 N/A
48 4 float A.f2 N/A
52 4 int A.i1 N/A
56 4 int A.i2 N/A
60 2 char A.c1 N/A
62 2 char A.c2 N/A
64 2 short A.s1 N/A
66 2 short A.s2 N/A
68 1 boolean A.bo1 N/A
69 1 boolean A.bo2 N/A
70 1 byte A.b1 N/A
71 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 VALUE
0 8 (object header: mark) N/A
8 4 (object header: class) N/A
12 4 java.lang.Boolean B.bo1 N/A
16 4 java.lang.Byte B.b1 N/A
20 4 java.lang.Character B.c1 N/A
24 4 java.lang.Double B.d1 N/A
28 4 java.lang.Float B.f1 N/A
32 4 java.lang.Integer B.i1 N/A
36 4 java.lang.Long B.l1 N/A
40 4 java.lang.Short B.s1 N/A
44 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 VALUE
0 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 VALUE
0 8 (object header: mark) N/A
8 4 (object header: class) N/A
12 4 int GrandFather.age N/A
16 8 double GrandFather.money N/A
24 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 VALUE
0 8 (object header: mark) N/A
8 4 (object header: class) N/A
12 4 int GrandFather.age N/A
16 8 double GrandFather.money N/A
24 8 double GrandFather.bbb N/A
32 8 double Father.money N/A
40 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 VALUE
0 8 (object header: mark) N/A
8 4 (object header: class) N/A
12 4 int GrandFather.age N/A
16 8 double GrandFather.money N/A
24 8 double GrandFather.bbb N/A
32 8 double Father.money N/A
40 8 double Father.ddd N/A
48 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 VALUE
0 8 (object header: mark) N/A
8 4 (object header: class) N/A
12 4 java.lang.Object Throwable.backtrace N/A
16 4 java.lang.String Throwable.detailMessage N/A
20 4 java.lang.Throwable Throwable.cause N/A
24 4 java.lang.StackTraceElement[] Throwable.stackTrace N/A
28 4 java.util.List Throwable.suppressedExceptions N/A
32 4 int Throwable.depth N/A
36 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 VALUE
0 8 (object header: mark) N/A
8 4 (object header: class) N/A
12 4 java.lang.reflect.Constructor Class.cachedConstructor N/A
16 4 java.lang.Class Class.newInstanceCallerCache N/A
20 4 java.lang.String Class.name N/A
24 4 java.lang.Module Class.module N/A
28 4 (alignment/padding gap)
32 4 java.lang.String Class.packageName N/A
36 4 java.lang.Class Class.componentType N/A
40 4 java.lang.ref.SoftReference Class.reflectionData N/A
44 4 sun.reflect.generics.repository.ClassRepository Class.genericInfo N/A
48 4 java.lang.Object[] Class.enumConstants N/A
52 4 java.util.Map Class.enumConstantDirectory N/A
56 4 java.lang.Class.AnnotationData Class.annotationData N/A
60 4 sun.reflect.annotation.AnnotationType Class.annotationType N/A
64 4 java.lang.ClassValue.ClassValueMap Class.classValueMap N/A
68 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