JVM的垃圾收集器

来源:互联网 发布:oppo手机mac地址查询 编辑:程序博客网 时间:2024/05/17 06:53


JVM中垃圾的回收由垃圾收集器进行,随着JDK的不断升级,垃圾收集器也开发出了各种版本,垃圾收集器不断优化的动力,就是为了实现更短的停顿。

下面是7种不同的分代收集器,如果两个收集器之间有连线,则表示它们之间可以搭配使用;所处的区域表示属于新生代还是老年代收集器。

1.Serial 收集器 (新生代)

最基本、历史最悠久(JDK1.3.1之前),这是一个单线程的收集器,当该收集器运行时必须暂停其他所有的工作线程,直到它收集结束。

收集过程:暂停所有线程

算法:复制算法

优点:简单高效,拥有很高的单线程收集效率

应用:Client模式下的默认新生代收集器

 

2.ParNew 收集器 (新生代)

Serial 的多线程版本,使用多线程进行垃圾收集

收集过程:与用户线程并发

算法:复制算法

优点:在CPU多的情况下,拥有比Serial更好的效果。单CPU环境下Serial效果更好

应用:许多运行在Server模式下的虚拟机中首选的新生代收集器

 

3.Parallel Scavenge 收集器(新生代)

Parallel Scavenge收集器的目标是达到一个可控制的吞吐量

吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间)

控制吞吐量的参数:最大垃圾收集停顿时间 -XX:MaxGCPauseMillis ; 直接设置吞吐量大小:-XX:GCTimeRatio。

MaxGCPauseMillis 的值为一个大于0的毫秒数, 最大停顿时间的缩短是以牺牲吞吐量和新生代空间来换取的。

GCTimeRatio 的值为一个大于0且小于100的整数。例如:-XX:GCTimeRatio=9 我们要求应用程序线程在整个执行时间中至少9/10是活动的(因此,GC线程占用其余1/10)

-XX:+UseAdaptiveSizePolicy:开启GC自适应调节策略,自动设置新生代大小、Eden与Survior区的比例、晋升老年代对象年龄等细节参数

应用:适合在后台运算而不需要太多交互的任务

 

4.Serial Old 收集器 (老年代)

Serial收集器的老年代版本,也是一个单线程的收集器,使用标记-整理算法

收集过程:暂停所有线程

算法:标记-整理算法

应用:主要意义是Client模式下的收集器,如果在Server模式下还有:a. JDK1.5之前的版本中与Parallel Scavenge搭配使用,b. 作为CMS收集器的后背预案在并发收集时使用

 

5. Parallel Old 收集器 (老年代)

Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。

收集过程:多线程

算法:标记-整理算法

应用:在注重吞吐量及CPU资源敏感的场合,可以优先考虑Parallel Scavenge加Parallel Old收集器

 

6. CMS 收集器 (老年代)

以获取最短回收停顿时间为目标,基于“标记-清除”算法

收集过程:初始标记-->并发标记-->重新标记-->并发清除

  初始标记、重新标记两个步骤仍需要“Stop The World” : 初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快;并发标记就是进行GC Roots Tracing的过程;重新标记是为了修正并发标记期间因用户程序继续运作,而导致标记产生变动的那一部分对象的标记记录。

  整个过程中耗时最长的是并发标记和并发清除,这两个过程都可以与用户线程一起工作。所以总体上说CMS收集器内存回收过程与用户线程一起并发执行。

算法:标记-清除 算法

缺点:1,对cpu资源敏感,默认启动的回收线程数是(cpu数量+3)/4,当cpu数较少的时候,会分掉大部分的cpu去执行收集器线程,影响用户,降低吞吐量。

     2,无法处理浮动垃圾,浮动垃圾即在并发清除阶段因为是并发执行,还会产生垃圾,这一部分垃圾即为浮动垃圾,要等下次收集。

         3,因为使用的是“标记-清除”算法,会产生碎片。

 

7. G1收集器 (整个Java堆:包括新生代和老年代)

特点:并行与并发、分代收集、空间整合、可预测的停顿

G1将整个java堆(包括新生代和老生代)划分为多个大小固定的独立区域,并跟踪这些区域的垃圾堆积程度,在后台维护一个优先列表,每次根据允许的收集时间,优先回收垃圾最多的区域。

  但这样回收有一个问题:对象分配在某个区域中,但并非只被本区域的其他对象引用,而是可以与整个Java堆任意的对象发生引用关系。在做可达性分析的时候,如何避免扫描整个堆呢?

  解决:区域之间的对象引用,以及其他收集器中新生代与老年代之间的对象引用,虚拟机都是使用Remembered Set来避免全堆扫描,在Remembered Set中记录对象的引用。

收集过程:初始标记-->并发标记-->最终标记-->筛选回收。与CMS不同的是,在最终标记阶段,需要停顿线程,但是可并发执行;筛选回收阶段,对各个区域的回收价值和成本    进行排序,按照用户所期望的回收时间进行垃圾回收,这个阶段也可以并发执行。

 


JVM中垃圾的回收由垃圾收集器进行,随着JDK的不断升级,垃圾收集器也开发出了各种版本,垃圾收集器不断优化的动力,就是为了实现更短的停顿。

下面是7种不同的分代收集器,如果两个收集器之间有连线,则表示它们之间可以搭配使用;所处的区域表示属于新生代还是老年代收集器。

1.Serial 收集器 (新生代)

最基本、历史最悠久(JDK1.3.1之前),这是一个单线程的收集器,当该收集器运行时必须暂停其他所有的工作线程,直到它收集结束。

收集过程:暂停所有线程

算法:复制算法

优点:简单高效,拥有很高的单线程收集效率

应用:Client模式下的默认新生代收集器

 

2.ParNew 收集器 (新生代)

Serial 的多线程版本,使用多线程进行垃圾收集

收集过程:与用户线程并发

算法:复制算法

优点:在CPU多的情况下,拥有比Serial更好的效果。单CPU环境下Serial效果更好

应用:许多运行在Server模式下的虚拟机中首选的新生代收集器

 

3.Parallel Scavenge 收集器(新生代)

Parallel Scavenge收集器的目标是达到一个可控制的吞吐量

吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间)

控制吞吐量的参数:最大垃圾收集停顿时间 -XX:MaxGCPauseMillis ; 直接设置吞吐量大小:-XX:GCTimeRatio。

MaxGCPauseMillis 的值为一个大于0的毫秒数, 最大停顿时间的缩短是以牺牲吞吐量和新生代空间来换取的。

GCTimeRatio 的值为一个大于0且小于100的整数。例如:-XX:GCTimeRatio=9 我们要求应用程序线程在整个执行时间中至少9/10是活动的(因此,GC线程占用其余1/10)

-XX:+UseAdaptiveSizePolicy:开启GC自适应调节策略,自动设置新生代大小、Eden与Survior区的比例、晋升老年代对象年龄等细节参数

应用:适合在后台运算而不需要太多交互的任务

 

4.Serial Old 收集器 (老年代)

Serial收集器的老年代版本,也是一个单线程的收集器,使用标记-整理算法

收集过程:暂停所有线程

算法:标记-整理算法

应用:主要意义是Client模式下的收集器,如果在Server模式下还有:a. JDK1.5之前的版本中与Parallel Scavenge搭配使用,b. 作为CMS收集器的后背预案在并发收集时使用

 

5. Parallel Old 收集器 (老年代)

Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。

收集过程:多线程

算法:标记-整理算法

应用:在注重吞吐量及CPU资源敏感的场合,可以优先考虑Parallel Scavenge加Parallel Old收集器

 

6. CMS 收集器 (老年代)

以获取最短回收停顿时间为目标,基于“标记-清除”算法

收集过程:初始标记-->并发标记-->重新标记-->并发清除

  初始标记、重新标记两个步骤仍需要“Stop The World” : 初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快;并发标记就是进行GC Roots Tracing的过程;重新标记是为了修正并发标记期间因用户程序继续运作,而导致标记产生变动的那一部分对象的标记记录。

  整个过程中耗时最长的是并发标记和并发清除,这两个过程都可以与用户线程一起工作。所以总体上说CMS收集器内存回收过程与用户线程一起并发执行。

算法:标记-清除 算法

缺点:1,对cpu资源敏感,默认启动的回收线程数是(cpu数量+3)/4,当cpu数较少的时候,会分掉大部分的cpu去执行收集器线程,影响用户,降低吞吐量。

     2,无法处理浮动垃圾,浮动垃圾即在并发清除阶段因为是并发执行,还会产生垃圾,这一部分垃圾即为浮动垃圾,要等下次收集。

         3,因为使用的是“标记-清除”算法,会产生碎片。

 

7. G1收集器 (整个Java堆:包括新生代和老年代)

特点:并行与并发、分代收集、空间整合、可预测的停顿

G1将整个java堆(包括新生代和老生代)划分为多个大小固定的独立区域,并跟踪这些区域的垃圾堆积程度,在后台维护一个优先列表,每次根据允许的收集时间,优先回收垃圾最多的区域。

  但这样回收有一个问题:对象分配在某个区域中,但并非只被本区域的其他对象引用,而是可以与整个Java堆任意的对象发生引用关系。在做可达性分析的时候,如何避免扫描整个堆呢?

  解决:区域之间的对象引用,以及其他收集器中新生代与老年代之间的对象引用,虚拟机都是使用Remembered Set来避免全堆扫描,在Remembered Set中记录对象的引用。

收集过程:初始标记-->并发标记-->最终标记-->筛选回收。与CMS不同的是,在最终标记阶段,需要停顿线程,但是可并发执行;筛选回收阶段,对各个区域的回收价值和成本    进行排序,按照用户所期望的回收时间进行垃圾回收,这个阶段也可以并发执行。

 

原创粉丝点击