美文网首页
对象什么时候会被GC

对象什么时候会被GC

作者: 风吹过那天 | 来源:发表于2019-08-22 10:57 被阅读0次

    前言

    在堆中几乎存放着所有的对象实例,垃圾收集器在对堆进行回收前,要判断哪些对象是“活着”的,哪些不可能再被任何途径所使用。首先介绍两种判读对象是否活着的算法。

    引用计数法

    • 概念:引用计数法是给对象中添加一个引用计数器,当有一个地方引用时,计数器加1,当引用失败,计数器值减1,任何时刻计数器为0的对象就是不可能再被使用的。(Java虚拟机没有采用该方法)
    • 优势:实现简单,判定效率高。
    • 缺点:无法解决对象之间相互循环引用的问题。

    可达性分析算法

    • 概念:通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用连,当一个对象到GC Roots没有任何引用链相连(GC Roots到这个对象不可达)时,证明这个对象是不可用的。


      可达性

    由上图可知通过GC Roots对象1,2,3,4均是可达的,而对象5,6,7通过GC Roots是不可达的,所以它们会被判定为可回收对象。

    • GC Roots对象包含以下几种:
      • 虚拟机栈中引用的对象
      • 方法区中类静态属性引用的对象
      • 方法区中常量引用的对象
      • 本地方法栈中JNI(一般说的Native方法)引用的对象

    生存还是死亡

    一个对象真正的死亡需要经历两次标记过程,若对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那它会被第一标记且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法,当对象没有覆盖finalize()方法,或该方法已经被虚拟机调用过,虚拟机将这两种情况视为“没有必要执行”。这类对象会被放置在yigeF-Queue队列中,由一个低优先级的线程去执行。稍后GC将对F-Queue中的对象进行第二次小规模的标记,若对象在finalize()方法中重新与引用链上的任意对象建立了关联,那在第二次标记时它将被移除出“即将回收的集合”,否则会被回收。


    垃圾回收算法

    • 标记-清除算法。首先标记出需要回收的对象,然后再统一回收。缺点:1、效率问题,标记和清除效率均不高;2、标记和清除两个过程会产生大量不连续的内存碎片。
    • 复制算法。首先将可用内存按照容量分为大小相等的两块,每次只使用一块,用完了就将还活着的对象复制到另一块上,然后把使用过的内存空间进行一次清理。优点:实现简单,运行效率高。缺点:将内存缩小为原来的一半。为了解决这个问题,提出将内存划分为一块较大的Eden空间和两块较小的Survivor空间,默认比例是8:1:1。工作机制是首先在Eden区创建对象,Eden区满了,经过一次GC后还活着的对象复制到Survivor0,清空Eden区;又开始在Eden区创建对象,再次满了,把Eden区和Survivor0中活着的对象copy到Survivor1,清空Eden和Survivor0;当对象太大或达到了在Survivor区最大移动次数都会被放到old空间。
    • 标记整理算法。标记过程与标记清除一样。后续步骤不是直接回收对象,而是让所有存活的对象移到一端,然后直接清理掉端边界以外的内存。
    • 分代收集算法。该算法是根据对象存活周期的不同划分为几块。一般是把Java堆分为新生代和老年代,根据各个年龄的特点采用最合适的收集算法。新生代采用复制算法,老年代采用标记-清除或者标记整理进行回收。

    HopSpot算法实现

    采用的是准确式GC,虚拟机可以知道哪些地方存着对象引用。采用一组称为OopMap的数据结构来达到这个目的。在类加载完成的时候,HotSpot就把对象内什么偏移量上什么类型的数据计算出来,在JIT编译过程中,也会在特定的位置记录下栈和寄存器中哪些位置是引用。

    安全点:前面提到的在“特定的位置”记录这些信息,这些位置称为安全点(Safepoint),即程序执行时并非在所有地方都能停顿下来GC,只有到达安全点时才能暂停。

    安全点的选定是以程序“是否有让程序长时间执行的特征”为标准进行选定的。对于Safepoint而言另一个问题是如何在GC发生时,所有线程都能跑到最近的安全点再停下来。有两种方案可以选择,分别是抢先式中断和主动式中断

    • 抢先式中断,在GC发生时首先把所有线程全部中断,如果发现有的线程没有在安全点上就恢复线程,让它跑到安全点上。该方法已经被弃用
    • 主动式中断,当GC需要中断线程的时候,不直接堆线程进行操作,仅仅简单设置一个标识,各个线程执行时主动区轮询这个标识,轮询标识的地方与安全点是重合的。

    安全区:是指在一段代码片段之中,引用关系不会发生变化,这个区域中的任意地方开始GC都是安全的。可以把Safe Region看作是被扩展了的安全点。主要是解决了当线程处于Sleep或者Blocked状态时,线程无法响应JVM中断请求,“走”到安全的地方去挂起的问题。

    相关文章

      网友评论

          本文标题:对象什么时候会被GC

          本文链接:https://www.haomeiwen.com/subject/odagsctx.html