jvm指针压缩指的是:
- java对象的Object Head里的
Klass Pointer
由8字节压缩为4字节,Klass Pointer指向当前对象的Class对象的内存位置 - java对象内部的复杂类型属性的引用由8字节压缩为4字节,就是说当前对象持有复杂类型的属性对象的内存位置
- 数组对象的长度由8字节压缩为4字节,同时数组对象持有的每个元素的内存引用由8字节压缩为4字节
以上机制生效的前提是jvm在64位
机器上,32位机器内存上限是4G,内存指针默认是4字节。
在64位机器上当分片的jvm内存小于32G
时,JVM默认开启指针压缩,此时指针表示的不是引用对象的实际内存地址,是一个相对的地址。如何用一个32位的数字表示内存32G内存内的任意地址?
,这个是我之前没想明白的,今天这篇博客主要就是解释这个问题。
在大于32G的机器上分配32G内存给jvm应用,首先这32G内存是需要连续的,因此jvm内存的低位相较于机器内存的低位有一个偏移量,计 offset0
,这个偏移量在通过内存指针计算实际内存地址时是需要考虑进去的。
32G内存有 1024*1024*1024*32 = 2^35
个字节,4个字节是32位,最大能表示 2^32-1
,这中间还差了 2^3
的数量级,该如何圆场?网上说4个字节有40多亿,表示40多亿个对象。但是一个对象至少16字节,2^32 * 16 = 2^36
超过了 2^35 的字节上限了,所以指针指向的也不是实际的对象地址。
后来想到了java对象大小是有一个对齐机制的:Object Header + 实例数据 + 对齐填充
。对齐填充保证java对象的大小始终是8字节的倍数
,也就是一个对象的大小都是8字节的倍数,他们有一个公约数8
。把 2^35 / 8 = 2^32
刚好等于4个字节所能表示的数值的区间。把jvm内存按8字节划分为一个一个格子区间,指针指向的是这个格子区间的序号
。
根据指针计算内存的实际地址的公式近视为: 内存地址(字节单位) = jvm内存偏移量 + 指针 * 8
通过这种巧妙的设计,能够将jvm内对象的地址从8字节压缩为4字节,极大的提高jvm对内存的使用效率。当jvm超过32G时,这种算法就失效了,在8字节的指针情况下,需要内存达到40-50G时才能和4字节指针时32G内存具有相同的存储空间。