垃圾回收

100 阅读5分钟

垃圾回收

  • 什么场景用什么垃圾回收策略

    • 对内存要求高的时候:想办法提高对象的回收效率,多回收掉一些对象,腾出更多内存
    • 在cpu使用率高的情况下:降低高并发时垃圾回收的频率,让CPU更多地去执行你的业务而不是垃圾回收
  • 对象什么时候被回收

    • 引用计数法
    • 可达性分析

垃圾什么时候回收呢?

  • 引用计数法

    1. 当存在对这个对象的引用时 计数加1 当计数为0时 对象被销毁

    2. 基于图的实现 邻接表 有边则为1

    3. 使用广度有限算法来回收内存

    4. 如果有环的话,如果发现节点在visited中存在,就是有环,可以手动删除一条边,将计数减1 再次从计数为0 的节点开始BFS回收

    • 缺点

      • 基于扫描的引用计数策略性能太慢 需要遍历
      • 每个对象多维护一个引用
      • GC和另外的线程的维护会产生竞争条件,会产生STOP the world
  • 可达性分析算法

  • 对象没有引用链和GcRoots连接
    if(对象有必要执行finalize(){
        将对象放入F-Queue,虚拟机自动创建低优先级线程finalizer执行对象的finaliz()
        if(对象在finalize()中重新建立连接){F-Queue移除  不回收
        }else{
            回收
        }
    }else{
    //对象未重写finalize()或虚拟机已经调用过finalize()
    回收
    }
    

    • 在栈中分配的对象存入一个集合中(root set)

    • 每次遍历Root set 中的对象 追溯到Heap中的对象进行分析

    • 分成两个阶段标记(mark)和(Sweep)

    • Mark负责标记在使用的对象

    • Sweep负责删除不被使用的对象

    • 那些对象可以当做CGroots

      1. 方法区静态属性引用的对象 全局对象的一种,Class对象本身很难被回收,回收的条件非常苛刻,只要Class对象不被回收,静态成员就不能被回收。
      2. 方法区常量池引用的对象 也属于全局对象,例如字符串常量池,常量本身初始化后不会再改变,因此作为GC Roots也是合理的。
      3. 方法栈中栈帧本地变量表引用的对象 属于执行上下文中的对象,线程在执行方法时,会将方法打包成一个栈帧入栈执行,方法里用到的局部变量会存放到栈帧的本地变量表中。只要方法还在运行,还没出栈,就意味这本地变量表的对象还会被访问,GC就不应该回收,所以这一类对象也可作为GC Roots。
      4. JNI本地方法栈中引用的对象 和上一条本质相同,无非是一个是Java方法栈中的变量引用,一个是native方法(C、C++)方法栈中的变量引用。
      5. 被同步锁持有的对象 被synchronized锁住的对象也是绝对不能回收的,当前有线程持有对象锁呢,GC如果回收了对象,锁不就失效了嘛。
    • 引用

      • 强引用

        • new object() 只要强引用在 永远不会回收被引用的对象
      • 软引用

        • 如SoftReference sr = new SofrReference<>(“hello”)
        • 用来描述一些有用但非必须得对象
        • 软引用 关联的对象,只有在内存不足的时候才会回收
        • 可以用来做缓存
      • 弱引用

        • 如weakReference sr = new weakReference<>(“hello”)
        • 弱引用也是用来描述非必须对象的
        • 无论内存是否充足,都会回收被弱引用关联的对象
      • 虚引用

        • ReferenceQueue queue = new ReferenceQueue<>();
        • PhantomReference pr = new PhantomReference<>(“hello”,queue);
        • 不影响对象的生命周期,如果一个对象只有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾收集器回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动,必须和引用队列(ReferenceQueue)配合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动
    • 三色标记法

      • 白色:尚未访问过
      • 黑色:本对象以及访问过,本对象引用到的其他对象也全部访问过了
      • 灰色:本对象已经访问过,但是本对象引用到的其他对象尚未完全访问完,全部访问后会变为黑色
    • 浮动垃圾

      • 三色标记法是并行的 在遍历时 灰色对象与黑色对象断开链接,但还是会进行遍历 , 多出来的本因该销毁的对象逃过本轮GC下次GC时再回收
    • 漏标(对象本因该存活但还是销毁)

      • 在遍历灰色对象时,1.将灰色对象指向白色对象的链接销毁,又2.新增了一条黑色对象指向此白色对象,此白色对象本因该存活但是会被销毁
      • 必须满足以上两个条件才会出现对象销毁但还是被使用
    • 解决方案

      • 原始快照

        • 破坏第一个条件 当灰色对象要删除白色对象的引用关系时,将这个要删除的引用记录下来,并发扫描结束后,在将这些记录重新扫描一次
      • 增量更新

        • 破坏第二条 在新增一条引用时,将该记录保存。