文章开头思考一个问题:为什么要计算对象大小
对象的大小
对象的内存布局
对象头
- Mark Word
32位机,占4字节;64位机,占8字节。
主要存储对象运行时的一部分数据: hashcode,GC 分代年龄,锁状态标志位,线程锁标记,偏向线程ID,偏向时间戳等 - 类型指针(Klass point)
指向方法区当前对象的Class类
开启指针压缩:指针大小为4B;未开启指针压缩:指针大小位8B - 数组长度
普通对象没有数组长度,数组对象才有数组长度 - 对齐填充(上第二个图,特殊情况下会有,见下面数组对象的演示)
数组对象在关闭指针压缩时,对象头会有填充
实例数据
对齐填充
JVM中所有对象大小都按8字节倍数对齐,填充的那部分就是填充
巩固上述内容,现在看一下空对象的大小
空对象:没有任何普通属性的类生成的对象
- 开启指针压缩 16B
=8B(Mark Word) + 4B(Klass point) + 0B(数组长度) + 0B(实例数据) + 4B(对齐填充)- 未开启指针压缩 16B
=8B(Mark Word) + 8B(Klass point) + 0B(数组长度) + 0B(实例数据) + 0B(对齐填充)
指针压缩(优化)
开启(默认)/关闭指针压缩 -XX:+/-UseCompressedOops
目的
通常64位JVM消耗的内存会比32位的大1.5倍,这是因为对象指针在64位架构下,长度会翻倍(更宽的寻址)。
对于那些将要从32位平台移植到64位的应用来说,多了1/2的内存占用
节省空间,提升jvm运行效率
指针压缩效果对比(注意区分对象的内存组成)
- 空对象
- 普通对象
- 数组对象
注意上例数组对象关闭指针压缩时,出现了两个alignment(对齐填充),为什么会有两个???
指针压缩实现原理
调优参数
-XX:+/-UseCompressedOops
(默认开启指针压缩)
oop:普通对象指针(ordinary object pointer),注意不是OOP(面向对象)
指针压缩原理
未开启指针压缩时,指针长度为8B,寻址单位是1B。
通过上文我们得知,jvm中对象的大小都是按8B对齐的,这能让我们有什么想法?
为什么我们不能将寻址单位扩大为对齐单位呢(想想,对齐单位的大小能调整吗?)
开启指针压缩时,指针长度为4B,寻址单位是8B(默认)
!!!!!希望能找到open jdk的实现逻辑来佐证一下,立个flag!!!!!!
思考1:开启指针压缩时,指针最大能表示的堆空间大小是多大?
指针压缩:指针8B长度(1B/单位) -> 4B长度(8B/单位)
- 8B长度指针支持的寻址内存大小是?
8B * 8bit/B = 64bit 寻址长度
2^64 * 1B ??操作系统的寻址位只用了48位,最大支持2^48的寻址单位,jvm能超了这个?疑问?同上,有时间找open jdk源码看一下吧,这里有个flag
反正支持的寻址内存很大!
- 4B长度指针支持的寻址内存大小是?
4B * 8bit/B = 32bit 寻址长度
2^32 * 8B = 32GB (32位操作系统支持最大4G内存,其寻址单位是1B,这里的单位是8B,那这里当然就是8*4G了)
哎呀,开启了指针压缩,堆支持的大小还变小了??
思考2:开启指针压缩时,若将堆内存大小设置成32G,会发生什么?
java -Xmx32g -XX:+PrintFlagsFinal hzw | grep -e UseCompressed
和下图命令同效果,因为默认开启指针压缩
上图除了Oop压缩以外,还发现一个ClassPointers?(普通对象指针和类指针)
什么现象?堆内存分配大于32G时,指针压缩设置失效了?为什么呢?
哈哈,我也不知道,我猜有两点
a. 8B指针寻址方式,内存范围更大,能超过32G;
b. (瞎猜)指针压缩的目的是防止新生代担保到老年代,从而导致频繁的fullGC。那你想想,这堆都分配了32G了,家里有钱何必再抠搜搜省这么点空间?
思考3:思考一下,能否通过开启指针压缩,然后扩大补齐单位从8B到16B,实现内存扩容支持?
我也不清楚了,就是有这个疑问(待确认)!
若扩大的补齐单位到16B的话,指令压缩支持内存是不是就能扩展到64G了,是否有jvm的参数控制这个?
》》为什么要计算对象大小
内存调优,一般是调整堆空间。堆空间里放的是对象,若要保证堆不频繁的进行Full GC,就要控制老年代的大小。
新生代占用过高时,容易发生内存担保,发生担保时新生代对象转移到老年代。
要防止新生代中的对象担保到老年代中,所以需要计算对象的大小并加以优化
内存担保概念在“阿里P8的这点Java底层?-JVM内存模型”中有提及