精确计算对象的大小——细就完事了
数据类型大小boolean1 bytesbyte1 bytesshort2 byteschar2 bytesint4 byteslong8 bytesfloat4 bytesdouble8 bytesOOPordinary object pointer普通对象指针启动CompressOops后,会压缩的对象:1.每个class的属性指针(静态成员变量)2.每个对象的属性指针3.普通对象数组的每个元
数据类型
- 基本数据类型
- 引用数据类型
基本数据类型
数据类型 | 大小 |
---|---|
boolean | 1 bytes |
byte | 1 bytes |
short | 2 bytes |
char | 2 bytes |
int | 4 bytes |
long | 8 bytes |
float | 4 bytes |
double | 8 bytes |
- 如果再方法体内定一,即在栈上分配
- 如果是类的成员变量,即在堆上分配
- 如果是类的静态成员变量,即在方法区上分配
引用数据类型
除了对象本身之外,它还存在一个指向他的引用(指针)
指针占用的内存在64位虚拟机上8个字节
如果开启指针压缩是4个字节
压缩指针规则
在堆中,32位的对象引用(指针)占4个字节,而64位的对象引用占8个字节。64位JVM在支持更大堆的同时,由于对象引用变大却带来了性能问题:
- 增加了GC开销:64位对象引用需要占用更多的堆空间,留给其他数据的空间将会减少,从而加快了GC的发生,更频繁的进行GC
- 降低CPU缓存命中率:64位对象引用增大了,CPU能缓存的oop将会更少,从而降低了CPU缓存的效率。
为了能够保持32位的性能,oop必须保留32位。那么,如何用32位oop来引用更大的堆内存呢?
答案是——压缩指针(CompressedOops)。
OOP:ordinary object pointer
普通对象指针
启动CompressOops后
会压缩的对象:
- 每个class的属性指针(静态成员变量)
- 每个对象的属性指针
- 普通对象数组的每个元素指针
不会压缩的对象:
- 指向PermGen的class对象指针
- 本地变量
- 堆栈元素
- 入参
- 返回值
- NULL指针
开启压缩
-XX:+UseCompressedOops
关闭压缩
-XX:-UseCompressedOops
打印jvm参数
-XX:+PrintFlagsFinal
winows打印jvm参数
java -XX:+PrintFlagsFinal -version |findstr UseCompressedOops
PS C:\Users\Think> java -XX:+PrintFlagsFinal -version |findstr UseCompressedOops
java version "1.8.0_111"
Java(TM) SE Runtime Environment (build 1.8.0_111-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.111-b14, mixed mode)
bool UseCompressedOops := true {lp64_product}
linux打印jvm参数
java -XX:+PrintFlagsFinal -version |grepUseCompressedOops
Zero Based Compressd Oops
零基压缩优化:针对压缩&解压动作的进一步优化
Zero Based Compressed Oops
指针压缩基本原理
1:通过对齐和偏移量将64位指针压缩成32位
2:零基压缩是针对压缩解压动作的进一步优化,他通过改变正常指针的随机地址分配特性,强制堆地址从零开始分配
启动零基压缩的前提:
分配给JVM的大小必须>4G&&<=32G
1:<4G,直接使用低虚拟地址空间(64位模拟32位),这样就不需要做压缩解压动作了
2:>4G&&<=32G,零基压缩
3:<32G,普通的对象指针压缩技术
实践
VM.current().details()
# Running 64-bit HotSpot VM.
# Using compressed oop with 0-bit shift.
# Using compressed klass with 3-bit shift.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
开启压缩指针:-XX:+UseCompressedOops
ClassLayout.parseClass(User.class).toPrintable()
com.dashuai.demo.service.order.entity.User object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 12 (object header) N/A
12 4 int User.age N/A
16 4 java.lang.Long User.id N/A
20 4 java.lang.String User.name N/A
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
关闭压缩指针:-XX:-UseCompressedOops
ClassLayout.parseClass(User.class).toPrintable()
com.dashuai.demo.service.order.entity.User object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 16 (object header) N/A
16 4 int User.age N/A
20 4 (alignment/padding gap)
24 8 java.lang.Long User.id N/A
32 8 java.lang.String User.name N/A
Instance size: 40 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total
header占用16个字节,之后的4个字节是对象的int属性,之后又4个字节的对齐填充。
由于HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,也就是说就是对象的大小必须是8字节的整数倍。
如上header加上fileld是36字节,所以需要4字节的对齐填充。
总结
- 优点:增加了Heap的大小
- 缺点:解压缩对于JVM来说性能有一定损耗
- 涉及知识点:ES性能优化(ES单个节点最大内存配置机制,此处具体不拓展)
- 大量对象的情况下,若想充分利用性能,设计类时需要细节考虑计算每个实体类所占jvm内存大小,将空间充分利用
引用
https://juejin.im/post/6844903873346453518#heading-14
更多推荐
所有评论(0)