JVM虚拟机---JVM的GC算法与种类

1、GC 

    垃圾收集 Garbage Collection 通常被称为“GC”,它诞生于1960年 MIT 的 Lisp 语言,经过半个多世纪,目前已经十分成熟了。

    jvm 中,程序计数器、虚拟机栈、本地方法栈都是随线程而生随线程而灭,栈帧随着方法的进入和退出做入栈和出栈操作,实现了自动的内存清理,因此,我们的内存垃圾回收主要集中于 java 堆和方法区中,在程序运行期间,这部分内存的分配和使用都是动态的.

2、对象存活判断 

    判断对象是否存活一般有两种方式:

        1、引用计数:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。此方法简单,无法解决对象相互循环引用的问题。

        2、可达性分析(Reachability Analysis):从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。不可达对象。

        在Java语言中,GC Roots包括:

            1、虚拟机栈中引用的对象。

            2、方法区中类静态属性实体引用的对象。

            3、方法区中常量引用的对象。

           4、 本地方法栈中JNI引用的对象。

3、垃圾收集算法  

    3.1标记 -清除算法

标记-清除算法(mark-and-sweep)

概念:标记-清除算法是现代垃圾回收算法的思想基础。是指在可以使用的内存被耗尽的时候,GC线程就会被触发并将程序暂停,进行标记-清除算法将垃圾回收,过程分为两个阶段:标记阶段和清除阶段。在标记阶段,首先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象。然后,在清除阶段,清除所有未被标记的对象。 
出现的问题:①.标记和清除过程效率不高 ,而且在进行GC的时候,需要停止应用程序,这会导致用户体验非常差劲。②.标记清除之后会产生大量不连续的内存碎片。失效对象都是随即的出现在内存的各个角落的,现在把它们清除之后,内存的布局自然会乱七八糟。

 
这里写图片描述

标记-压缩算法(mark-compact)

概念:标记-压缩算法适合用于存活对象较多的场合,如老年代。它在标记-清除算法的基础上做了一些优化。和标记-清除算法一样,标记-压缩算法也首先需要从根节点开始,对所有可达对象做一次标记。但之后,它并不简单的清理未标记的对象,而是将所有的存活对象压缩到内存的一端,之后,清理边界外所有的空间。 
出现的问题:标记-压缩算法的缺点就是效率也不高,不仅要标记所有存活对象,还要整理所有存活对象的引用地址。 
这里写图片描述

复制算法(copying)

概念:将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,之后,清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收. 
出现的问题:用空间换取时间,空间浪费,不适用于存活对象较多的场合,如老年代等。 
这里写图片描述

分代收集算法(generational collecting)

“分代收集”(Generational Collection)算法,把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清理”或“标记-整理”算法来进行回收。 
这里写图片描述
分代收集过程

4、垃圾收集器

串行收集器

最古老、最稳定、效率高的收集器,缺点是可能会产生较长时间的停顿,她只使用一个线程去回收,没有办法发挥多核计算机的性能。 
-XX:+UseSerialGC 
新声代和老年代使用串行回收,新生代使用复制算法、老年代使用标记-压缩算法。 
这里写图片描述

并行收集器

ParNew

-XX:UserParNewGC 
新生代使用并行回收,老年代使用串行回收。 
新生代的的并行回收采用复制算法,多线程回收(多核支持),可以使用XX:ParallelGCThreads限制线程的数量。当然,多线程并不一定快。

Parallel

类似于ParNew,新声代采用复制算法、老年代使用标记-压缩算法,更加关注吞吐量。

所谓吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量=运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间)。

                停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能够提升用户的体验;

                而高吞吐量则可以最高效率地利用CPU时间,尽快地完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。

                可以通过参数来打开自适应调节策略,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或最大的吞吐量;也可以通过参数控制GC的时间不大于多少毫秒或者比例;新生代复制算法、老年代标记-压缩

 
-XX:+UseParallelGC 使用Parallel收集器和老年代串行。 
-XX:+UserParalleOldGC 使用Paralle收集器老年代并行。 
-XX:MaxGCPauseMills 最大停顿时间,单位毫秒,GC尽力保证收回时间不超过设定的值,但不是肯定。 
-XX:GCTimeRatio 在0-100中取值,代表垃圾收集时间占总时间的比,默认99,代表最大允许1%的时间做GC。 
这里写图片描述

CMS收集器

Concurrent Mark Sweep 并发(与用户线程一起执行)标记清除,这是一个老年代的收集器(新声代使用的是ParNew),GC中应用线程停顿时间比较短,所以并发阶段会降低吞吐量。 
-XX:+UseConcMarkSweepGC

CMS收集器GC过程

  1. 初始标记 
    根可以直接关联到的对象,速度快
  2. 并发标记(和用户线程一起) 
    主要标记过程,标记全部对象
  3. 重新标记 
    由于并发标记时,用户线程依然运行,因此在正式清理前,再做修正
  4. 并发清除(和用户线程一起) 
    基于标记结果,直接清理对象 
    这里写图片描述

CMS收集器特点

  1. 尽可能降低停顿
  2. 会影响系统整体吞吐量和性能。比如,在用户线程运行过程中,分一半CPU去做GC,系统性能在GC阶段,反应速度就下降一半。
  3. 清理不彻底。因为在清理阶段,用户线程还在运行,会产生新的垃圾,无法清理。
  4. 不能在空间快满时再清理,因为和用户线程一起运行。-XX:CMSInitiatingOccupancyFraction设置触发GC的阈值,如果不幸内存预留空间不够,就会引起concurrent mode failure。

CMS收集器常用参数

-XX:+UseCMSCompactAtFullCollection Full GC,进行一次整理,整理的过程是独占的,停顿时间较长。 
-XX:+CMSFullGCsBeforeCompaction 设置几次 Full GC后进行一次一次碎片整理。 
-XX:ParallelCMSThreads 设定CMS的线程数量,一般约等于可用CPU的数量,不宜设置太大。

GC的参数整理

-XX:+UseSerialGC:在新生代和老年代使用串行收集器 
-XX:SurvivorRatio:设置eden区大小和survivior区大小的比例 
-XX:NewRatio:新生代和老年代的比 
-XX:+UseParNewGC:在新生代使用并行收集器 
-XX:+UseParallelGC :在新生代使用并行回收收集器 
-XX:+UseParallelOldGC:老年代使用并行回收收集器 
-XX:ParallelGCThreads:设置用于垃圾回收的线程数 
-XX:+UseConcMarkSweepGC:新生代使用并行收集器,老年代使用CMS+串行收集器 
-XX:ParallelCMSThreads:设定CMS的线程数量 
-XX:CMSInitiatingOccupancyFraction:设置CMS收集器在老年代空间被使用多少后触发 
-XX:+UseCMSCompactAtFullCollection:设置CMS收集器在完成垃圾收集后是否要进行一次内存碎片的整理 
-XX:CMSFullGCsBeforeCompaction:设定进行多少次CMS垃圾回收后,进行一次内存压缩 
-XX:+CMSClassUnloadingEnabled:允许对类元数据进行回收 
-XX:CMSInitiatingPermOccupancyFraction:当永久区占用率达到这一百分比时,启动CMS回收 
-XX:UseCMSInitiatingOccupancyOnly:表示只在到达阀值的时候,才进行CMS回收

G1(Garbage First)垃圾回收器

        G1(Garbage First)垃圾收集器是当今垃圾回收技术最前沿的成果之一。早在JDK7就已加入JVM的收集器大家庭中,成为HotSpot重点发展的垃圾回收技术。同优秀的CMS垃圾回收器一样,G1也是关注最小时延的垃圾回收器,也同样适合大尺寸堆内存的垃圾收集,官方也推荐使用G1来代替选择CMS。G1最大的特点是引入分区的思路,弱化了分代的概念,合理利用垃圾收集各个周期的资源,解决了其他收集器甚至CMS的众多缺陷。

        并行与并发:G1能充分利用多CPU、多核环境下的硬件优势,使用多个CPU(CPU或者CPU核心)来缩短Stop-The-World停顿的时间,部分其他收集器原本需要停顿Java线程执行的GC动作,G1收集器仍然可以通过并发的方式让Java程序继续执行。

        分代收集:与其他收集器一样,分代概念在G1中依然得以保留。虽然G1可以不需其他收集器配合就能独立管理整个GC堆,但它能够采用不同的方式去处理新创建的对象和已经存活了一段时间、熬过多次GC的旧对象以获取更好的收集效果。

        空间整合:与CMS的“标记-清理”算法不同,G1从整体看来是基于“标记-整理”算法实现的收集器,从局部(两个Region之间)上看是基于“复制”算法实现,无论如何,这两种算法都意味着G1运作期间不会产生内存空间碎片,收集后能提供规整的可用内存。这种特性有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前触发下一次GC。

        可预测的停顿:这是G1相对于CMS的另外一大优势,降低停顿时间是G1和CMS共同的关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒,这几乎已经是实时Java(RTSJ)的垃圾收集器特征了。

        参数控制:-XX:+UseG1GC 使用G1垃圾收集器    -XX:ParallelGCThreads 限制线程数量    -XX:MaxGCPauseMillis 指定最大停顿时间

猜你喜欢

转载自blog.csdn.net/qq_30904985/article/details/82219326