- 垃圾回收器
-
Serial收集器: 针对新生代的单线程收集器,使用复制算法。收集垃圾是会产生较长时间的停顿,但不会产生线程切换的开销
-
Serial Old收集器: 针对老年代的单线程收集器,使用标记整理算法。
-
Parallel Scavenge收集器: jdk 1.8默认的垃圾回收器。新生代回收器,采用复制算法,多线程并行回收,充分利用CPU资源。进行垃圾收集时,必须暂停所有工作线程,直到完成
-
Parallel Old收集器: 是Parallel Scavenge收集器的老年代版本,使用多线程,标记-整理算法。
-
ParNew收集器: Serial收集器的多线程版本,也需要stop the world,复制算法。
-
CMS(Concurrent Mark Sweep) 收集器:
- 并发低停顿收集器
- 使用标记清除算法
- 四个阶段
- 初始标记 (标记GC Roots可以直接关联的对象,速度很快)
- 并发标记 (进行GC Roots Tracing,判断对象是否存活)
- 重新标记 (校准并发标记对象的存活状态)
- 并发清除 (回收标记的对象)
- 初始标记和重新标记仍然需要Stop The World
- CMS缺点
- 由于并发带来的CPU资源消耗
- 由于并发收集在回收过程中产生的浮动垃圾无法清除
- 使用标记清除算法带来的空间碎片问题
- 四个阶段
-
G1收集器:
- 在G1与其他收集器的区别就是它不再区分新生代或者老年代,它将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,它们都是一部分Region(不需要连续)的集合。
-
G1把Java堆分为多个Region,就是“化整为零”。但是Region不可能是孤立的,一个对象分配在某个Region中,可以与整个Java堆任意的对象发生引用关系。在做可达性分析确定对象是否存活的时候,需要扫描整个Java堆才能保证准确性,这显然是对GC效率的极大伤害。
-
为了避免全堆扫描的发生,虚拟机为G1中每个Region维护了一个与之对应的Remembered Set。虚拟机发现程序在对Reference类型的数据进行写操作时,会产生一个Write Barrier暂时中断写操作。
检查Reference引用的对象是否处于不同的Region之中(在分代的例子中就是检查是否老年代中的对象引用了新生代中的对象),如果是,便通过CardTable把相关引用信息记录到被引用对象所属的Region的Remembered Set之中。当进行内存回收时,在GC根节点的枚举范围中加入Remembered Set即可保证不对全堆扫描也不会有遗漏。
如果不计算维护Remembered Set的操作,G1收集器的运作大致可划分为以下几个步骤:
- 初始标记(Initial Marking) 仅仅只是标记一下GC Roots 能直接关联到的对象,并且修改TAMS(Nest Top Mark Start)的值,让下一阶段用户程序并发运行时,能在正确可以的Region中创建对象,此阶段需要停顿线程,但耗时很短。
- 并发标记(Concurrent Marking) 从GC Root 开始对堆中对象进行可达性分析,找到存活对象,此阶段耗时较长,但可与用户程序并发执行。
- 最终标记(Final Marking) 为了修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录,虚拟机将这段时间对象变化记录在线程的Remembered Set Logs里面,最终标记阶段需要把Remembered Set Logs的数据合并到Remembered Set中,这阶段需要停顿线程,但是可并行执行。
- 筛选回收(Live Data Counting and Evacuation) 首先对各个Region中的回收价值和成本进行排序,根据用户所期望的GC 停顿是时间来制定回收计划。此阶段其实也可以做到与用户程序一起并发执行,但是因为只回收一部分Region,时间是用户可控制的,而且停顿用户线程将大幅度提高收集效率。
- CMS收集器和G1收集器的区别:
- CMS收集器是老年代的收集器,可以配合新生代的Serial和ParNew收集器一起使用;
- G1收集器收集范围是老年代和新生代,不需要结合其他收集器使用;
- CMS收集器以最小的停顿时间为目标的收集器;
- G1收集器可预测垃圾回收的停顿时间
- CMS收集器是使用“标记-清除”算法进行的垃圾回收,容易产生内存碎片
- G1收集器使用的是“标记-整理”算法,进行了空间整合,降低了内存空间碎片。