jvm的压缩指针

153 阅读3分钟

首先明确一个概念,32位和64位,指的是在内存寻址时,内存地址编码分别为32bit或者64bit,所以最大分别支持2^32和2^64个地址。

32位和64位也分3个层次:

  1. CPU的32位和64位之分(大家应该听说过8位单片机和16位单片机);
  2. 操作系统的32位和64位(win xp就分32位和64位两种版本);
  3. JVM的32位和64位。 内存中每个地址存放 1byte(8bit) 数据(不要问为什么是8bit,规定),所以32位系统的内存最大为 2^32 * 8bit = 4GB;64位系统则最大支持 4GB*2^32,这个数字很大,目前来说你都不会用到这么大。

计算机想要从内存读取数据,就要知道该数据在内存中的地址,一个内存地址在32/64位系统中分别需要32/64bit来编码,即这个地址要占用 4/8 byte。而这个地址本身也存储在内存中,消耗内存空间。(CPU读取第一个数据时使用的地址哪来的?问操作系统,问bios,问CPU电路)

1. 什么是指针压缩

这里主要是讲JVM的压缩指针。CPU和操作系统做不做这个压缩操作,不去探究。64位jvm中指针(对象的引用,表示对象的内存地址)本来应该是 8byte,但是太占空间,于是使用某种方法压缩到 4byte。

2. 为什么要压缩

假设当JVM所在的宿主机由32位换成64位操作系统时(此时CPU也是64位CPU),内存条由4G加到8G。此时正常来说jvm需要使用64bit地址代替32bit地址,各种java对象的引用(指针)的大小都增大了1倍,由 4byte 变成了 8byte。如果不做任何处理,实际使用时8G内存可能还不如4G内存+32位系统时够用。

3. 如何压缩?

JVM地址不再覆盖内存地址所有控件,而是每隔 8 byte 记录一个内存地址,这样,JVM使用32bit,即最大2^32 的地址,就可以表示实际内存的 2^32 * 8 = 2^35 个地址,即 2^35 = 32GB。超过32GB 时压缩指针失效,一方面也是因为32GB内存足够抵消指针大小翻倍带来的负向效果。
当JVM的地址存入到CPU寄存器时,左移3位;当JVM从CPU寄存器读地址时,右移3位。这样,JVM 4GB的寻址能力,映射到CPU就达到了32GB,代价是每8个实际内存地址jvm只能记录开头的第一个。

4. 为什么可以这么压缩?

java中对象的实际内存占用实行8byte对齐。假设一个对象A实际大小需要占用20byte,此时下一个对象B不会从21byte处开始写入,而是从25开始,让A对象占用24byte空间,保持8的倍数。因此JVM压缩指针后,每隔8byte取一个地址并不会丢失实际有用的内存地址。