6.3 HotSpot堆
HotSpot JVM 的代码相当复杂,由一个解释器、一个即时编译器和一个用户空间内存管理子系统组成。HotSpot JVM 的代码使用 C 和 C++ 编写,还有相当多针对特定平台的汇编代码。
现在,我们来总结一下什么是 HotSpot 堆,再回顾一下它的基本特性。Java 堆是一块连续的内存,在启动 JVM 时创建,但一开始只会把部分堆分配给各个内存池。在应用运行的过程中,内存池会按需扩容。扩容由垃圾回收子系统完成。
堆中的对象
应用线程在 Eden 区创建对象,不确定性垃圾回收循环会移除这些对象。这个垃圾回收循环在需要时(即内存不够用时)才会运行。堆分为两代:新生代和老年代。新生代由三个区组成:Eden 区和两个 Survivor 区;而老年代只有一个内存空间。
多次垃圾回收循环后存活下来的对象,最终会推给老年代。只回收新生代的回收操作消耗(所需计算)往往不大。HotSpot 使用的标记清除算法比目前为止我们见到的要高级,而且还会做额外的簿记,提升垃圾回收的性能。下一节介绍老年代,以及 HotSpot 如何处理生命期较长的对象。
6.3.1 回收老年代
讨论垃圾回收程序时,开发者还要知道两个重要的术语。
- 并行回收程序
使用多个线程执行回收操作的垃圾回收程序。
- 并发回收程序
可以和应用线程同时运行的垃圾回收程序。
到目前为止,我们见到的回收程序都是并行回收程序,而不是并发回收程序。默认情况下,老年代使用的回收程序也是并行标记清除回收程序(而不是并发回收程序),但是,HotSpot 允许植入不同的回收程序。例如,稍后在本节会见到 HotSpot 内置的 CMS 回收程序,这是并行回收程序,但基本上也是并发回收程序。
乍看起来,老年代默认使用的回收程序和新生代使用的回收程序类似,但二者有个重要的区别:老年代默认使用的回收程序不是筛选回收程序。回收老年代时,回收程序会整理老年代。这一点很重要,这样内存空间在使用的过程中不会产生碎片。
6.3.2 其他回收程序
这一节完全针对 HotSpot,但不会深入介绍,因为超出本书范畴了。不过你要知道,还有其他一些回收程序存在。如果不使用 HotSpot,你应该阅读 JVM 的文档,看看有什么其他选择。
1. 并发标记清除
HotSpot 中最常使用的替代回收程序是并发标记清除(Concurrent Mark and Sweep,CMS)回收程序。这个回收程序只能用来回收老年代,与一个回收新生代的并行回收程序配合使用。
CMS 只适用于需要短暂停顿的应用,这些应用的停顿时间不能超过 STW 的几毫秒。这类应用极少,除了金融贸易类应用之外,很少有应用真正需要这么短的停顿时间。
CMS 是个很复杂的回收程序,往往很难有效调校。CMS 是个非常有用的工具,但部署时不能掉以轻心。CMS 有一些基本特性(如下所示)你要知道,但详细说明已经超出本书范畴。有兴趣的读者可以阅读专门的博客和邮件列表(例如,“Friends of jClarity”邮件列表经常讨论 GC 性能方面的问题)。
CMS 只能回收老年代;
在多数 GC 循环中,CMS 都和应用线程一起运行,以便减少停顿时间;
应用线程不会像之前那样停顿很久;
分为六个阶段,都是为了缩减 STW 停顿时间;
把一次 STW 长停顿变成两次(往往很短的)STW 停顿;
簿记工作更多,CPU 时间也更长;
总体来说,GC 循环的时间更长;
默认情况下,并发运行时,GC 使用一半 CPU;
除了需要短暂停顿的应用之外,不要使用 CMS;
绝对不能在吞吐量大的应用中使用;
不会整理内存,如果内存碎片很多,会回滚到默认的(并行)回收程序。
2. G1
Garbage First 回收程序(简称 G1)是一个新的垃圾回收程序,在 Java 7 时代开发(Java 6 时代完成了部分准备工作)。G1 是一种短暂停顿回收程序,目的是取代 CMS。而且 G1 允许用户设定停顿指标,指定回收垃圾时停顿多久,以及多久停顿一次。和 CMS 不同的是,G1 适用于吞吐量较高的应用场合。
G1 使用粗粒度方式管理内存,把内存分成多个区,集中精力管理几乎充满垃圾的区,因为这些区释放的内存最多。G1 是一种筛选回收程序,筛选各区时,会不断整理内存。
新开发一个通用的生产级回收程序,不是个简单的过程。因此,虽然 G1 已经开发了数年,但在 2014 年年初,多数评测仍显示 G1 没有 CMS 效率高。话虽如此,不过二者之间的差距在稳步减小,而且在某些应用场合中,G1 已经处于领先地位。在未来数月或数年中,G1 完全可能会变成最常用的短暂停顿回收程序。
最后,HotSpot 还有一个 Serial 回收程序(和 SerialOld 回收程序),以及一个“增量式 CMS”回收程序。这些回收程序都废弃了,不要再使用。
CMS 只适用于需要短暂停顿的应用,这些应用的停顿时间不能超过 STW 的几毫秒。这类应用极少,除了金融贸易类应用之外,很少有应用真正需要这么短的停顿时间。