zoukankan      html  css  js  c++  java
  • 垃圾回收

    年轻代(1/3)和老年代(2/3)

    • 年轻代和老年代是堆的结构,垃圾回收也主要回收堆
    • 年轻代 对象创建之后很快被回收
    • 老年代 对象长期存在

    触发条件

    • 新生代里的对象太多,空间满了,就会触发垃圾回收,把没人引用的对象回收

    回收条件

    • 用可达性分析法
      • 根搜索算法是从离散数学中的图论引入的,程序把所有的引用关系看作一张图,从一个节点GC ROOT开始,寻找对应的引用节点,找到这个节点以后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕之后,剩余的节点则被认为是没有被引用到的节点,即无用的节点
    • 可以作为GC Root的对象
      • 静态变量(这里的变量也指引用)
      • 局部变量(这里的变量也指引用)
    • 总结
      • 只要你的对象被方法的局部变量和类的静态变量引用了,对象就不会被回收
    • 案例
      • 下述代码中,如果垃圾回收,会回收ReplicaFetcher对象吗?为什么?
        • 不会的,因为ReplicaFetcher对象被ReplicaManager对象中的实例变量引用了,然后ReplicaManager对象被Kafka类的静态变量给引用了
        • 重点:经过可达性算法计算,主要一个对象的GC ROOT是静态变量或者局部变量,它就不会被回收

    引用类型

    • 强引用 必须用的,不会被回收
    • 软引用 可有可无,内存实在不够,就回收它
    • 弱引用
    • 虚引用

    finalize()方法(了解)

    • 属于object方法
    • 要被回收了,会调用finalize()方法,看看是否把这个对象给了某个GC ROOT变量,如果有GC ROOT重新引用了自己,就不会被回收
    • 总结
      • finalize可以监听一个对象被回收,但是不能保证调用了finalize的对象一定会被回收,同时一个对象在第二次标记回收时是不会触发finalize的!如果想绝对监听一个对象是否被回收,只有在JVM里面添加参数-XX:+PrintGCDetails分析GC日志

    垃圾回收算法

    • 标记清除算法
    • 复制算法
    • 标记整理算法
    • 分代收集算法

    标记清除算法(新生代用)

    • 首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象
    • 优缺点
      • 容易造成内存碎片
      • 效率慢,标记和清除两个过程的效率都不高

    复制算法(新生代用)

    • 现在的商业虚拟机都采用这种收集算法来回收新生代,研究表明,新生代中的对象 98%是“朝生夕死”的,所以并不需要按照 1:1 的比例来划分内存空间,而是将内存分为一块较大的 Eden 空间和两块较小的 Survivor 空间,每次使用 Eden 和其中一块 Survivor。 Survivor from 和Survivor to ,内存比例 8:1:1。当回收时,将 Eden 和 Survivor 中还存活着的对象一次性地复制到另外一块 Survivor 空间上,最后清理掉 Eden 和刚才用过的 Survivor 空间。HotSpot 虚拟机默认 Eden 和 Survivor 的大小比例是 8:1, 也就是每次新生代中可用内存空间为整个新生代容量的 90% (80%+10%),只有 10% 的内存会被“浪费”。当然,90%的对象可回收只是一般场景下的数据,我们没有办法保证每次回收都只有不多于 10%的对象存活,当 Survivor 空间不够用时,需要依赖其他内存(这里指老年代)进行分配担保(Handle Promotion)
    • 过程
      1 把Eden区中的存活对象都标记出来,然后全部转移到Survivor1去,接着一次性清空掉Eden中的垃圾对象
      2 当Eden再次塞满的时候,就又要触发Minor GC了,此时已然是垃圾回收线程运行垃圾回收器中的算法逻辑,也就是采用复制算法逻辑,去标记出来Eden和Survivor1中的存活对象
      3 然后一次性把存活对象转移到Survivor2中去,接着把Eden和Survivor1中的垃圾对象都回收掉
      4 循环以上过程
    • 优点
      • 只有10%的内存空间是被闲置的,90%的内存都被使用上了

    新生代对象什么时候进入老年代?

    • 经过15次垃圾回收,该对象还没有被回收进入老年代;默认是15次,可通过-XX:MaxTenuringThreshold参数设置
    • 动态年龄判断
      • 年龄1 + 年龄2 + ... + 年龄n 的多个对象大小总和超过survivor区的50%,此时就会把年龄大于等于n的对象都放入老年代
    • 大对象直接进入老年代
      • 有一个JVM参数,就是“-XX:PretenureSizeThreshold”,可以把他的值设置为字节数,比如“1048576”字节,就是1MB
      • 他的意思就是,如果你要创建一个大于这个大小的对象,比如一个超大的数组,或者是别的啥东西,此时就直接把这个大对象放到老年代里去。压根儿不会经过新生代

    Minor GC老年代分配担保原则

    • Minor GC时,新生代存活的对象,survivor区放不下了,这些对象直接放入老年代
    • 在任何一次Minor GC之前,jvm都会检查老年代的可用空间,是否大于新生代对象的总大小
      • 如果老年代可用空间大于新生代对象总大小,并且Minor GC存活的对象,大于survivor区大小,minor gc存活的对象直接进入老年代
      • 如果发现老年代的可用内存已经小于了新生代的全部对象大小了,就会看一个-XX:-HandlePromotionFailure的参数是否设置了,如果有这个参数,那么就会继续尝试进行下一步判断。下一步判断,就是看看老年代的内存大小,是否大于之前每一次Minor GC后进入老年代的对象的平均大小
        • 如果大于,就进行Minor GC
        • 如果参数没设置,或者老年代可用空间小于之前每一次Minor GC后进入老年代的对象的平均大小,此时就会直接触发一次Full GC,就是对老年代进行垃圾回收,尽量腾出来一些内存空间,然后再执行Minor GC
    • Minor GC可能发生三种情况
      • 存活对象小于survivor区,直接进入survivor区
      • 存活对象大于survivor区,小于老年代可用空间,进入老年代
      • 存活对象大于老年代,会发生OOM内存溢出(这种情况发生之前,一定判断了是否有-XX:-HandlePromotionFailure参数,可能发生了FULL GC

    老年代回收算法(标记整理算法)

    • 标记-整理算法,标记过程仍然与标记-清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存

    FULL GC出现时机

    • Minor GC之前,一通检查发现很可能Minor GC之后要进入老年代的对象太多了,老年代放不下,此时需要提前触发Full GC然后再带着进行Minor GC,这里有两种情况
      • 老年代可用内存小于新生代全部对象的大小,如果没开启空间担保参数,会直接触发Full GC,所以一般空间担保参数都会打开
      • 老年代可用内存小于历次新生代GC后进入老年代的平均对象大小,此时会提前Full GC
    • Minor GC之后,发现剩余对象太多放入老年代都放不下了

    FULL GC比新生代垃圾回收慢10倍,jvm优化就是调整参数,不让FULL GC频繁发生



    本文参考救火队长jvm专栏

  • 相关阅读:
    倍增算法2(树上倍增)
    倍增算法1
    可持久线段树
    【BZOJ】1059: [ZJOI2007]矩阵游戏(二分图匹配)
    【BZOJ】2743: [HEOI2012]采花(树状数组)
    【BZOJ】2959: 长跑(lct+缩点)(暂时弃坑)
    【学习笔记】LCT link cut tree
    【学习笔记】FFT
    【BZOJ】1001: [BeiJing2006]狼抓兔子(最小割 / 对偶图)
    【BZOJ】1007: [HNOI2008]水平可见直线(凸包)
  • 原文地址:https://www.cnblogs.com/wasitututu/p/13625882.html
Copyright © 2011-2022 走看看