JDK1.8 默认使用 Parallel Scavenge(年轻代) + Serial Old(老年代)垃圾回收(堆+方法区)
垃圾回收都会stop the world,区别在于时间长短,无论是串行还是并行都会挂起用户线程。然而CMS、G1在并发标记的时候不会挂起用户线程,其他时候会挂起。
系统吞吐量有要求时,使用 Parallel Scavenge垃圾收集器。
扫描对象比复制对象省时得多!
GC ROOT对象可达
- 栈中引用的对象
- 方法区类的静态属性引用的对象
- 方法区中的常量的引用对象
- 本地方法栈的JNI的引用对象
垃圾回收算法发展历程
标记-清理 ——> 复制算法 ——> 标记-整理 ——> 分代收集
垃圾回收算法
标记-清理
标记-清除算法
老年代回收垃圾算法,分为标记和清除两个阶段,先标记出所有需要回收的对象,标记完成后统一回收所有被标记的对象。
缺点:标记和清除两个过程效率都不高;标记清除之后会产生大量不连续的内存碎片。
为了解决标记清除算法内存碎片化严重的缺陷,提出了复制算法。
复制算法
复制算法
将可用的内存分成两份,每次使用其中一块,当这块回收之后把未回收的复制到另一块内存中,然后把使用的清除。这种算法运行简单,解决了标记-清除算法的碎片问题,但是这种算法代价过高,需要将可用内存缩小一半,对象存活率较高时,需要持续的复制工作,效率比较低。
标记-整理算法
结合了以上两个算法,为了避免缺陷而提出。标记阶段和标记清理算法相同,标记后不是清理对象,而是将存活对象移向内存的一端,然后清除端边界外的对象。
分代收集
把堆内存分为新生代和老年代,新生代又分为 Eden 区、From Survivor 和 To Survivor。一般新生代中的对象基本上都是朝生夕灭的,每次只有少量对象存活,因此采用复制算法,只需要复制那些少量存活的对象就可以完成垃圾收集;老年代中的对象存活率较高,就采用标记-清除和标记-整理算法来进行回收。
相对而言,老年代每次只回收少量对象,回收次数相对少,采用标记整理算法。
- 对象的内存分配主要在新生代的 Eden Space 和 Survivor Space 的 From Space(Survivor 目前存放对象的那一块),少数情况会直接分配到老年代;
- 当新生代的 Eden Space 和 From Space 空间不足时就会发生一次 GC,进行 GC 后,EdenSpace 和 From Space 区的存活对象会被挪到 To Space,然后将 Eden Space 和 FromSpace 进行清理;
- 如果 To Space 无法足够存储某个对象,则将这个对象存储到老年代;
- 在进行 GC 后,使用的便是 Eden Space 和 To Space 了,如此反复循环;
- 当对象在 Survivor 区躲过一次 GC 后,其年龄就会 +1。默认情况下年龄到达 15 的对象会被移到老年代中。
垃圾回收器
serial垃圾回收器
- 新生代:Serial 垃圾收集器是 JVM 运行在 Client 模式下默认的新生代垃圾收集器;
- 复制算法 :Serial 是最基本垃圾收集器,使用复制算法,曾经是 JDK1.3.1 之前新生代唯一的垃圾收集器;
- 单线程:Serial 只会使用一个 CPU 或一条线程去完成垃圾收集工作;
- 线程暂停:Serial 在进行垃圾收集的时,必须暂停其他所有的工作线程,直到垃圾收集结束;
- 单线程效率最高:Serial 对于限定单个 CPU 环境来说,没有线程交互的开销,可以获得最高的单线程垃圾收集效率。
ParNew 垃圾回收器
- 新生代:ParNew 垃圾收集器是很多 JVM 运行在 Server 模式下新生代的默认垃圾收集器;
- 复制算法 : ParNew 和 Serial 一样使用了复制算法;
- 多线程 :ParNew 垃圾收集器其实是 Serial 收集器的多线程版本;
- 线程暂停:ParNew 和 Serial 一样垃圾收集的同时,必须暂停其他所有的工作线程;
- 默认线程数:ParNew 收集器默认开启和 CPU 数目相同的线程数,可以通过-XX:ParallelGCThreads 参数来限制垃圾收集器的线程数。
Parallel Scavenge回收器
- 新生代:Parallel Scavenge 收集器也是一个新生代垃圾收集器;
- 复制算法:Parallel Scavenge 收集器同样使用复制算法;
- 多线程 :Parallel Scavenge 收集器也是一个多线程的垃圾收集器;
- 高吞吐量:Parallel Scavenge 重点关注的是程序达到一个可控制的吞吐量(Thoughput,吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间)),高吞吐量可以最高效率地利用 CPU 时间,尽快地完成程序的运算任务;
- 适用场景:主要适用于在后台运算而不需要太多交互的任务。自适应调节策略也是 ParallelScavenge 收集器与 ParNew 收集器的一个重要区别。
Serial Old
- 老年代:Serial Old 是 Serial 垃圾收集器老年代的版本;
- 标记-整理算法:Serial Old 使用标记-整理算法;
- 单线程:Serial Old 与 Serial 一样是单线程收集器;
- Client 模式:JVM 运行在 Client 模式下,Serial Old 是默认的老年代垃圾收集器;
- Server 模式:JVM 运行在 Server 模式下,Serial Old 主要有两个用途:
- 在 JDK1.5 之前版本中与新生代的 Parallel Scavenge 收集器搭配使用;
- 作为年老代中使用 CMS 收集器的后备垃圾收集方案。
Parallel Old垃圾回收器
- 老年代:Parallel Old 收集器是 Parallel Scavenge 的老年代版本;
- 标记-整理算法:Parallel Old 收集器使用标记-整理算法;
- 多线程:Parallel Old 收集器是多线程收集器;
- JDK1.6:Parallel Old 是 JDK1.6才开始提供的。
CMS垃圾回收器
- 老年代:CMS(Concurrent mark sweep)收集器是一种年老代垃圾收集器;
- 标记-清理算法:和其他年老代使用标记-整理算法,CMS 使用标记-清除算法;
- 多线程 :CMS 采用的是多线程的标记-清除算法;
- 停顿时间端: CMS最主要目标是获取最短垃圾回收停顿时间,最短的垃圾收集停顿时间可以为交互比较高的程序提高用户体验。
G1垃圾回收器
- 无分代:G1 将新生代,老年代的物理空间划分取消了。这样我们再也不用单独的空间对每个代进行设置了,不用担心每个代内存是否足够;
- 标记-整理算法:G1 收集器采用标记-整理算法,无内存碎片产生;
- 分区回收:G1 虽然没有了新生代与老年代的物理限制,但是 G1 采取内存分区策略,将堆内存划分为大小固定的几个独立区域。在分区中,同时存在新生代与老年代;