jvm学习笔记2:jvm垃圾收集器与垃圾收集算法

在了解垃圾收集算法与垃圾收集器之前,应该知道在jvm中该如何确定一个对象是否还存活?因为垃圾收集器只收集那些已经死去的对象(不可能再被任何其他的对象所使用的对象)
  1. 对象标记算法
    1. 引用计数算法:

      给对象添加一个引用计数器,每当一个地方引用它时,计数器值+1,当引用失效时,计数器值-1。当计数器为0时,该对象就是不可能再被使用的了。该算法实现简单,效率也很高,但是java没有使用这种算法,主要原因是它很难解决对象之间的相互循环引用的情况,比如 obj a 与obj b 都互相持有彼此的实例,然后将obj a 与obj b都赋值为null,此时,obj a 与 obj b的引用都不在使用了,但是其内部都还持有彼此的引用所以它们的引用计数还是1,所以引用计数器无法通知垃圾收集器回收它们

    2. 可达性分析算法:

      • 通过一些了 “GC Roots”的对象作为起点,从这些起点向下搜索,搜索所经过的路径成为引用链,当一个对象到“GC Roots”对象没有任何引用链相连,则确定该对象是不可能在被使用这样即使对象相互引用,但是如果它没有与GC Roots”对象引用链相连,那么也可以被回收jvm使用是这种算法。

      • 在java中作为GC Roots”对象可以是:

        1. 虚拟机栈( 栈帧中的本地变量表中)引用的对象(方法中的局部对象变量)
        2. 方法区中类静态属性的引用的变量(静态对象变量)
        3. 方法区中常量的引用的对象(常量对象变量)
        4. 本地方法栈中的引用的对象
  2. 引用类型
    • 无论是引用计数器还是可达性分析算法都是通过引用来判断对象的存活的,在jdk1.2之前,引用的定义是:如果一个reference类型的数据中存储着另一块内存的起始地址,就称这块内存代表着一个引用。所有对象只有引用与没有被引用两种状态,它们的回收机制都是一样的,那就是没有被引用了才能回收。而有时我们希望在内存不够时,能先回收一些我们知道的以后不会再用到的对象,所以在jdk1.2之后对引用的概念进行了扩充,引入了下面几种不同的引用类型,它们在被回收时强度依次逐渐减弱
      1. 强引用: 默认的引用,类似obj a = new obj()就是强引用,只要引用还存在,垃圾收集器就不会回收掉对象

      2. 软引用(SoftReference): 使用软引用关联的对象,在系统将要发生内存溢出的情况时,将会对该引用关联的对象列入回收范围并在进行第二次回收,

      3. 弱引用(weakReference): 使用弱引用关联的对象,强度比软引用要弱,被弱引用关联的对象只能存活在下一次垃圾收集发生之前,当垃圾收集器工作时,无论当前内存是否足够,都会回收掉被弱引用关联的对象

      4. 虚引用(PhantomReference): 对象关联虚引用,完全不会对其生存时间构成引用,它唯一的目的就是在这个关联的对象被垃圾收集器回收时收到一个系统通知

  3. 垃圾收集算法
    1. 标记-清除算法

      • 最基础的就是该算法,后面的几种算法都是在此算法的基础之上改进的,标记-清除算法如它名字一样,分为两个阶段,第一阶段对可以进行回收的对象进行标记,第二阶段将前一阶段标记的对象清除。至于那些对象是可以回收的前面已经记录过了。标记-清除算法有两个缺点,第一效率问题,标记和清除的过程效率都不高,第二空间利用率问题,在回收的过程会产生大量的不连续的内存碎片,内存碎片太多可能会导致当程序分配一个大对象时找不到足够的连续内存来保存而不得不触发一次垃圾回收动作
    2. 复制算法

      • 复制算法为了解决效率问题而出现,它将可用内存分为两块,每次只使用其中一块,当这块的内存用完了,就将不需要回收的对象复制到另一块内存上面,然后将已使用的这块的内存进行清理。内存分配时也不用考虑内存碎片的问题了,只要在每次分配的时候移动堆指针按顺序分配内存即可。不过这种算法由于一次只使用一半的内存来存放对象导致内存利用率不高,在jvm堆中的新生代就是使用这种算法来进行垃圾收集的,由于在新生代中对象的生命周期并不是很长,所以在新生代并是不按1:1来进行内存分配的,而是将内存分配为一块较大的Eden空间与两块较小的Suvivor空间,它们的大小比例默认是8:1:1,每次使用eden与一块suvivor空间,当回收时,将不需要回收的对象放入另一块未使用的suvivor空间中,但是有时候可能不需要回收的对象内存大于未使用的suvivor空间内存,那么这个时候就需要依赖老年代来进行内存分配担保。将这些不需要回收的对象直接通过内存分配担保机制放入老年代中
    3. 标记-整理算法

      • 由于复制算法不想按1:1的内存分配的话,就需要有额外的分配担保机制来应对不需要回收的对象内存大小超过未被使用的内存大小这种情况,而jvm堆中的老年代中的对象往往存活时间都比较长,复制大量对象不止效率低,而且还需要进行内存分配担保机制,所以老年代不能直接使用复制算法。根据老年代的特点提出了标记-整理算法,它的标记过程仍然跟”标记-清除算法”一样,但是后续步骤不是对可回收对象直接进行清理,而是让所有不可回收对象向一段移动,然后直接清理掉端边界意外的内存空间
    4. 分代收集算法:

      • 所有商业虚拟机都采用该算法,这种算法只是根据对象的生命周期不同讲内存分为几块,一般把java堆分为为新生代与老年代,这样可以根据各个年代的特点采用不同的算法,在新生代中,由于对象生命周期不长,经常只有少量对象不需要被回收,所以采用复制算法,只需要复制少量对象就可以完成收集。在老年代因为对象生命周期比较长,大部分都不需要被回收,而且还没有额外的内存空间对它进行分配担保,所以必须使用”标记-整理”或者”标记-清除”算法来进行回收
  4. 垃圾收集器

    1. Serial

      • 单线程的垃圾收集器,在进行垃圾收集时需要暂停所有的工作线程,知道垃圾收集完成。在client模式下的新生代的默认收集器,对其他收集器来说,它是简单而高效的。
    2. ParNew

      • Serial垃圾收集器的多线程版本,除了使用多线程进行垃圾收集外,其余的一切都跟Serial完全一样,在server模式下首选的新生代收集器,因为它是除了Serial外,唯一 一个可以配合CMS垃圾收集器工作的新生代垃圾收集器
    3. Parallel Scavenge:

      • 是多线程,又是并行(多条垃圾收集器线程同时工作,用户线程等待)的垃圾收集器,与其他垃圾收集器关注尽可能的缩短用户线程等待时间不同,它的关注目标是达到一个可控的吞吐量,吞吐量表示的是用户代码运行时间与cup总消耗时间的比值,即吞吐量=用户代码时间/(用户代码时间+垃圾收集时间)
    4. Serial Old:

      • Serial收集器的老年代收版本, 基于”标记-整理”算法。
    5. Parallel Old:

      • Parallel Scavenge收集器的老年代版本,使用多线程与”标记-整理”算法,jdk1.6提供的垃圾收集器,在这之前由于Parallel Scavenge只能搭配Serial Old使用,由于Serial Old是单线程的,所以server模式下在性能上有所拖累,直到Parallel Old出来后,Parallel Scavenge与Parallel Old收集器配合才有了比较好的组合,在注重吞吐量与cpu敏感的场合下可以优先考虑使用这个组合
    6. CMS :

      • CMS收集器是一种以最短回收停顿时间的垃圾收集器,适应于服务响应时间短,系统时间停顿短的应用。它是一种基于”标记-清除”算法的垃圾收集器,它的垃圾收集运作过程相对于前面几种垃圾收集器来说更加复杂,整个垃圾收集过程分为4个步骤:

        1. 初始标记

        2. 并发标记

        3. 重复标记

        4. 并发清除

      • 其中初始标记与重复标记仍需要暂停所有用户线程,初始标记只是标记能够跟”GC Roots”关联的对象,并发标记是对与”GC Roots”对象上的引用链相关联的对象进行跟踪,重复标记是修正并发标记期间,因为用户线程继续运行而产生变动的那一部分对象的标记记录。最后在并发的清除需要回收的对象
      • 它的主要优点就是并发收集,停顿低。但是它还有三个显著的缺点
        1. 对cpu资源非常敏感,并发阶段,虽然不会导致用户线程停顿,但是因为占用了一部分线程,所以会导致用户线程变慢,吞吐量变低 , CMS默认启动的线程时(cpu数量+3)/4,也就是当4cpu超过4个以上时,垃圾收集线程最多占用不超过25%的cpu资源,但是当cpu不足4个时,那么CMS对用户线程的影响会很大,如果本来cpu负载就很大还要分出一半的线程去执行垃圾收集,那么会导致用户程序执行速度变慢。
        2. CMS垃圾收集器无法处理浮动垃圾,并发清除过程中,用户线程运行所产生的垃圾,这些垃圾出现在标记过程之后,所以CMS无法再本次垃圾收集中处理它们,只好留着下一次清除,这些垃圾就叫”浮动垃圾”,由于CMS垃圾收集的过程中用户线程也在运行,所以需要预留一部分内存空间给用户线程使用,默认情况下,在内存使用了68%的时候,CMS垃圾收集器会被激活,可以通过-XX:CMDInitiatingOccupancyFraction的值来提供出发百分比,而当垃圾回收进行中,预留给用户线程的那部分内存空间不能满足用户程序的需要时,就会出现一次”Concurrent Mode Failure”失败,这个时候jvm将会启动后配方案:临时启用Serial Old收集器来重新进行一次老年代的垃圾收集。这样停顿时间就更长了
        3. 因为是基于”标记-清除”算法的,所以CMS垃圾收集器在回收完成后会有大量的内存碎片,内存碎片过高,会导致大对象无法分配,而触发一次一次老年代垃圾回收(Full GC),为了解决这个问题,CMS收集器提供了一个-XX:CMSCompactAtFullConllection开关参数,用户在Full GC服务后额外的一次碎片整理过程,这个过程不是并发的,所以会导致停顿时间变长。
          其次还提供了一个-XX:CMSFullGCsBefoCompaction参数用于设置多少次不压缩的full gc后,进行一次带压缩的。
    7. G1:

      • G1收集器是基于”标记-整理”算法的收集器,不会产生内存碎片,它可以非常精确的控制停顿,其他垃圾收集器的收集范围分为新生代与老年代,而G1将整个堆分为多个大小固定的独立区域,并且跟踪每个区域的垃圾厚度,维护一个优先列表,每次根据允许的时间,优先回收垃圾最多的区域,这也是G1(Garbage First)的名字由来
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值