G1 总览
首先是内存划分上,之前介绍的分代收集器将整个堆分为年轻代、老年代和永久代,每个代的空间是确定的。
而 G1 将整个堆划分为一个个大小相等的小块(每一块称为一个 region),每一块的内存是连续的。和分代算法一样,G1 中每个块也会充当 Eden、Survivor、Old 三种角色,但是它们不是固定的,这使得内存使用更加地灵活。
执行垃圾收集时,和 CMS 一样,G1 收集线程在标记阶段和应用程序线程并发执行,标记结束后,G1 也就知道哪些区块基本上是垃圾,存活对象极少,G1 会先从这些区块下手,因为从这些区块能很快释放得到很大的可用空间,这也是为什么 G1 被取名为 Garbage-First 的原因。
在 G1 中,目标停顿时间非常非常重要,用 -XX:MaxGCPauseMillis=200 指定期望的停顿时间。
G1 使用了停顿预测模型来满足用户指定的停顿时间目标,并基于目标来选择进行垃圾回收的区块数量。G1 采用增量回收的方式,每次回收一些区块,而不是整堆回收。
我们要知道 G1 不是一个实时收集器,它会尽力满足我们的停顿时间要求,但也不是绝对的,它基于之前垃圾收集的数据统计,估计出在用户指定的停顿时间内能收集多少个区块。
注意:G1 有和应用程序一起运行的并发阶段,也有 stop-the-world 的并行阶段。但是,Full GC 的时候还是单线程运行的,所以我们应该尽量避免发生 Full GC,后面我们也会介绍什么时候会触发 Full GC。
G1 内存占用
注:这里不那么重要。
G1 比 ParallelOld 和 CMS 会需要更多的内存消耗,那是因为有部分内存消耗于簿记(accounting)上,如以下两个数据结构:
- Remembered Sets:每个区块都有一个 RSet,用于记录进入该区块的对象引用(如区块 A 中的对象引用了区块 B,区块 B 的 Rset 需要记录这个信息),它用于实现收集过程的并行化以及使得区块能进行独立收集。总体上 Remembered Sets
消耗的内存小于 5%。 - Collection Sets:将要被回收的区块集合。GC 时