目录介绍

  • 01.检测垃圾算法有哪些
  • 02.什么是引用计数法
  • 03.什么是可达性分析算法
  • 04.标记可达对象

好消息

  • 博客笔记大汇总【16年3月到至今】,包括Java基础及深入知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计N篇[近100万字,陆续搬到网上],转载请注明出处,谢谢!
  • 链接地址:https://github.com/yangchong211/YCBlogs
  • 如果觉得好,可以star一下,谢谢!当然也欢迎提出建议,万事起于忽微,量变引起质变!

01.检测垃圾算法有哪些

  • 垃圾收集器一般必须完成两件事:检测出垃圾;回收垃圾。怎么检测出垃圾?一般有以下几种方法:引用计数法,可达性分析算法。
  • 注意有时候通常将可达性分析算法称为根搜索算法。

02.什么是引用计数法

  • a.引用计数法:
    • 给一个对象添加引用计数器,每当有个地方引用它,计数器就加1;引用失效就减1。好了,问题来了,如果我有两个对象A和B,互相引用,除此之外,没有其他任何对象引用它们,实际上这两个对象已经无法访问,即是我们说的垃圾对象。但是互相引用,计数不为0,导致无法回收。
    • 引用计数收集器可以很快的执行,并且交织在程序运行中,对程序需要不被长时间打断的实时环境比较有利,但其很难解决对象之间相互循环引用的问题。
  • 引用计数的优点:
    • 垃圾收集器可以很快地执行,当一个对象的引用数为0时就可以回收这个对象,垃圾收集交织在程序的正常执行过程中,不用长时间中断程序的正常执行。
  • 引用计数的缺点:
    • 1.每次引用计数的增加和减少会带来额外的开销
    • 2.无法检测出循环引用
    • 总结一下: 难以检测出对象之间的循环引用。同时,引用计数器增加了程序执行的开销。所以Java语言并没有选择这种算法进行垃圾回收。

03.什么是可达性分析算法

  • 可达性分析算法
    • 以根集对象为起始点进行搜索,如果有对象不可达的话,即是垃圾对象。这里的根集一般包括java栈中引用的对象、方法区常量池中引用的对象,本地方法中引用的对象等。
  • 什么是根集
    • 所谓根集(Root Set)就是正在执行的Java程序可以访问的引用变量(注意:不是对象)的集合(包括局部变量、参数、类变量),程序可以使用引用变量访问对象的属性和调用对象的方法。
  • 这种算法的基本思路:
    • (1)通过一系列名为“GC Roots”的对象作为起始点,寻找对应的引用节点。
    • (2)找到这些引用节点后,从这些节点开始向下继续寻找它们的引用节点。
    • (3)重复(2)。
    • (4)搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,就证明此对象是不可用的。

04.标记可达对象

  • JVM中用到的所有现代GC算法在回收前都会先找出所有仍存活的对象。根搜索算法是从离散数学中的图论引入的,程序把所有的引用关系看作一张图。下图3.0中所展示的JVM中的内存布局可以用来很好地阐释这一概念:
  • 首先,垃圾回收器将某些特殊的对象定义为GC根对象。
    • 所谓的GC根对象包括:
    • (1)虚拟机栈中引用的对象(栈帧中的本地变量表);
    • (2)方法区中的常量引用的对象;
    • (3)方法区中的类静态属性引用的对象;
    • (4)本地方法栈中JNI(Native方法)的引用对象。
    • (5)活跃线程。
  • 接下来,垃圾回收器会对内存中的整个对象图进行遍历,它先从GC根对象开始,然后是根对象引用的其它对象,比如实例变量。回收器将访问到的所有对象都标记为存活。
  • 存活对象在上图中被标记为蓝色。当标记阶段完成了之后,所有的存活对象都已经被标记完了。其它的那些(上图中灰色的那些)也就是GC根对象不可达的对象,也就是说你的应用不会再用到它们了。这些就是垃圾对象,回收器将会在接下来的阶段中清除它们。关于标记阶段有几个关键点是值得注意的:
    • (1)开始进行标记前,需要先暂停应用线程,否则如果对象图一直在变化的话是无法真正去遍历它的。暂停应用线程以便JVM可以尽情地收拾家务的这种情况又被称之为安全点(Safe Point),这会触发一次Stop The World(STW)暂停。触发安全点的原因有许多,但最常见的应该就是垃圾回收了。
    • (2)暂停时间的长短并不取决于堆内对象的多少也不是堆的大小,而是存活对象的多少。因此,调高堆的大小并不会影响到标记阶段的时间长短。
    • (3)在根搜索算法中,要真正宣告一个对象死亡,至少要经历两次标记过程:
      • 1.如果对象在进行根搜索后发现没有与GC Roots相连接的引用链,那它会被第一次标记并且进行一次筛选。筛选的条件是此对象是否有必要执行 finalize()方法(可看作析构函数,类似于OC中的dealloc,Swift中的deinit)。当对象没有覆盖finalize()方法,或finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都视为没有必要执行。
      • 2.如果该对象被判定为有必要执行finalize()方法,那么这个对象将会被放置在一个名为F-Queue队列中,并在稍后由一条由虚拟机自动建立的、低优先级的Finalizer线程去执行finalize()方法。finalize()方法是对象逃脱死亡命运的最后一次机会(因为一个对象的finalize()方法最多只会被系统自动调用一次),稍后GC将对F-Queue中的对象进行第二次小规模的标记,如果要在finalize()方法中成功拯救自己,只要在finalize()方法中让该对象重新引用链上的任何一个对象建立关联即可。而如果对象这时还没有关联到任何链上的引用,那它就会被回收掉。
    • (4)实际上GC判断对象是否可达看的是强引用。
  • 当标记阶段完成后,GC开始进入下一阶段,删除不可达对象。

其他介绍

01.关于博客汇总链接

02.关于我的博客