2.2 ZGC内存管理
ZGC为了能高效、灵活地管理内存,实现了两级内存管理:虚拟内存和物理内存,并且实现了物理内存和虚拟内存的映射关系。这和操作系统中虚拟地址和物理地址设计思路基本一致。ZGC主要的改进点就是重新定义了虚拟内存和物理内存的映射关系。
在介绍内存管理之前,我们先从一个问题出发。我们知道ZGC目前仅支持64位Linux,最多管理4TB的内存。不知道你有没有注意到这个地方似乎有点问题,64位系统支持的内存远超过4TB,那么为什么我们一直强调它只能支持4TB的内存,为什么不使用更多的虚拟内存?
要回答这个问题,首先要了解ZGC的内存管理机制。ZGC对整个内存空间进行划分,这是来自于源码中关于地址空间的一个说明,如图2-1所示。
图2-1 ZGC地址空间设计
在图2-1中,特别提到了3个视图,分别是Marked0、Marked1和Remapped,而且有趣的是这3个视图会映射到操作系统的同一物理地址。这就是ZGC中Color Pointers的概念,通过Color Pointers来区别不同的虚拟视图。
在ZGC中常见的几个虚拟空间有[0~4TB)、[4TB~8TB)、[8TB~12TB)和[16TB~20TB)。其中[0~4TB)对应的是Java的堆空间;[4TB~8TB)、[8TB~12TB)和[16TB ~20TB)分别对应Marked0、Marked1和Remapped这3个视图。这几个区域有什么关系?我们先看图2-2:
图2-2 ZGC中虚拟地址和物理地址映射关系
图2-2是ZGC在运行时虚拟地址和物理地址的转化。从图中我们可以得到:
❑4TB是理论上最大的堆空间,其大小受限于JVM参数。
❑0~4TB的虚拟地址是ZGC提供给应用程序使用的虚拟空间,它并不会映射到真正的物理地址。
❑操作系统管理的虚拟内存为Marked0、Marked1和Remapped这3个空间,且它们对应同一物理空间。
❑在ZGC中这3个空间在同一时间点有且仅有一个空间有效。为什么这么设计?这就是ZGC的高明之处,利用虚拟空间换时间;这3个空间的切换是由垃圾回收的不同阶段触发的,详见4.2.2节。
❑应用程序可见并使用的虚拟地址为0~4TB,经ZGC转化,真正使用的虚拟地址为[4TB~8TB)、[8TB~12TB)和[16TB~20TB),操作系统管理的虚拟地址也是[4TB~8TB)、[8TB~12TB)和[16TB~20TB)。应用程序可见的虚拟地址[0~4TB)和物理内存直接的关联由ZGC来管理。
这里涉及的一个最关键的知识点就是多视图的映射,这是ZGC高效回收垃圾的基础,垃圾回收的相关内容将在第4章和第5章介绍。这里我们介绍一下ZGC是如何实现多视图映射的。
ZGC支持64位系统,我们看一下ZGC是如何使用64位地址的。ZGC中低42位(第0~41位)用于描述真正的虚拟地址(这就是图2-2中提到的应用程序可以使用的堆空间),接着的4位(第42~45位)用于描述元数据,其实就是大家所说的Color Pointers,还有1位(第46位)目前暂时没有使用,最高17位(第47~63位)固定为0,具体如图2-3所示。
图2-3 ZGC地址
由于42位地址最大的寻址空间就是4TB,这就是ZGC一直宣称自己最大支持4TB内存的原因。这里还有视图的概念,Marked0、Marked1和Remapped就是3个视图,分别将第42、43、44位设置为1,就表示采用对应的视图。在ZGC中,这4位标记位的目的并不是用于地址寻址的,而是为了区分Marked0、Marked1和Remapped这3个视图。当然对于操作系统来说,这4位标记位代表了不同的虚拟地址空间,操作系统在寻址的时候会把标记位和虚拟地址结合使用。在介绍ZGC的多视图映射之前,我们先回顾一下如何实现多视图映射。
提示
我们还可以从另外一个角度来考虑为什么ZGC目前设计为支持4TB的内存管理。这是由于X86_64处理器硬件的限制,目前X86_64处理器地址线只有48条,也就是说64位系统支持的地址空间为256TB。为什么处理器的指令集是64位的,但是硬件仅支持48位的地址?最主要的原因是成本问题,即便到目前为止由48位地址访问的256TB的内存空间也是非常巨大的,也没有多少系统有这么大的内存,所以在设计CPU时仅仅支持48位地址,可以少用很多硬件。如果未来系统需要扩展,则无须变更指令集,只需要从硬件上扩展即可。对于ZGC来说,由于多视图(Color Pointers)的缘故,会额外占用4位地址位,所以真正可用的应该是44位,理论上ZGC可以支持16TB的内存。目前支持的4TB只是人为的限制,很容易扩展到16TB,但是如果要扩展超过16TB时,则需要重新设计这一部分。