环境: 本机为64位操做系统。jdk 1.8数组
1、对象在内存中存储的布局能够分为3块区域:对象头(Header),实例数据(Instance Data),对齐填充(Padding)。ruby
2、咱们能够经过maven工程引用对应的 jol-core jar包。将对应的内存分布打印出来作相应的研究。架构
我这里写勒一个类:User,4个字段 占用 4*4 byte =16 byte 共16字节 (实例数据占用16字节)并发
ObjectHeader类的打印对象的总体分布:主要代码以下:jvm
System.out.println(ClassLayout.parseInstance(user).instanceSize());maven
System.out.println(ClassLayout.parseInstance(user).toPrintable());ide
产生结果:oop
上图中:能够看到,对象在内存中分为3块区域。 对齐填充 在JVM中定义为 8byte 的 整数倍。 因此 28 % 8 =4 因此须要padding 4 byte 。布局
Header包含以下内容:主要由 Mark World (8byte)+ Class Pointer (4byte) (类型指针)优化
1. Mark World 根据 对象 lock 标记的状态不一样 , 所存储的内容也会发生改变. 具体以下图.
|------------------------------------------------------------------------------|--------------------| | Mark Word (64 bits) | State | |------------------------------------------------------------------------------|--------------------| | unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 | lock:2 | Normal | |------------------------------------------------------------------------------|--------------------| | thread:54 | epoch:2 | unused:1 | age:4 | biased_lock:1 | lock:2 | Biased | |------------------------------------------------------------------------------|--------------------| | ptr_to_lock_record:62 | lock:2 | Lightweight Locked | |------------------------------------------------------------------------------|--------------------| | ptr_to_heavyweight_monitor:62 | lock:2 | Heavyweight Locked | |------------------------------------------------------------------------------|--------------------| | | lock:2 | Marked for GC | |------------------------------------------------------------------------------|--------------------|
biased_lock | lock | 状态 |
---|---|---|
0 | 01 | 无锁 |
1 | 01 | 偏向锁 |
0 | 00 | 轻量级锁 |
0 | 10 | 重量级锁 |
0 | 11 | GC标记 |
lock:2位的锁状态标记位,因为但愿用尽量少的二进制位表示尽量多的信息,因此设置了lock标记。该标记的值不一样,整个mark word表示的含义不一样。 biased_lock:对象是否启用偏向锁标记,只占1个二进制位。为1时表示对象启用偏向锁,为0时表示对象没有偏向锁。
age:4位的Java对象年龄。在GC中,若是对象在Survivor区复制一次,年龄增长1。当对象达到设定的阈值时,将会晋升到老年代。默认状况下,并行GC的年龄阈值为15,并发GC的年龄阈值为6。因为age只有4位,因此最大值为15,这就是-XX:MaxTenuringThreshold
选项最大值为15的缘由。
identity_hashcode:25位的对象标识Hash码,采用延迟加载技术。调用方法System.identityHashCode()
计算,并会将结果写到该对象头中。当对象被锁定时,该值会移动到管程Monitor中。
thread:持有偏向锁的线程ID。
epoch:偏向时间戳。
ptr_to_lock_record:指向栈中锁记录的指针。
ptr_to_heavyweight_monitor:指向管程Monitor的指针。
2. Class Pointer (类型指针) 这一部分用于存储对象的类型指针,该指针指向它的类元数据,JVM经过这个指针肯定对象是哪一个类的实例。该指针的位长度为JVM的一个字大小,即32位的JVM为32位,64位的JVM为64位。
若是应用的对象过多,使用64位的指针将浪费大量内存,统计而言,64位的JVM将会比32位的JVM多耗费50%的内存。为了节约内存能够使用选项+UseCompressedOops
开启指针压缩,其中,oop即ordinary object pointer普通对象指针。开启该选项后,下列指针将压缩至32位:
- 每一个Class的属性指针(即静态变量)
- 每一个对象的属性指针(即对象变量)
- 普通对象数组的每一个元素指针
上例证:
禁用指针压缩: -XX:-UseCompressedOops
开启指针压缩,jvm默认开启
固然,也不是全部的指针都会压缩,一些特殊类型的指针JVM不会优化,好比指向PermGen的Class对象指针(JDK8中指向元空间的Class对象指针)、本地变量、堆栈元素、入参、返回值和NULL指针等。
3. 若是对象是一个数组,那么对象头还须要有额外的空间用于存储数组的长度,这部分数据的长度也随着JVM架构的不一样而不一样:32位的JVM上,长度为32位;64位JVM则为64位。64位JVM若是开启+UseCompressedOops
选项,该区域长度也将由64位压缩至32位。