美文网首页
GC概要串烧

GC概要串烧

作者: rock_fish | 来源:发表于2021-01-16 23:02 被阅读0次

    对象的迁徙 从幼儿园(新生代)到养老院(老年代)

    • 一般情况:

    对象优先分配在新生代
    新生代如果对象满了,会触发Minor GC(Young GC)回收掉没有被引用的垃圾对象
    如果有对象躲过了十多次垃圾回收,就会放入老年代里
    如果老年代也满了,那么也会触发垃圾回收,把老年代里没人引用的垃圾对象清理掉

    • 复杂情况:

    新生代垃圾回收之后,因为存活对象太多,导致大量对象直接进入老年代
    特别大的超大对象直接不经过新生代就进入老年代
    动态对象年龄判断机制
    空间担保机制

    回收机制

    1. 无GCRoots引用的对象回收。
    2. 有GCRoots引用
      2.1 强引用不回收;
      2.2 软引用,空间不够依然回收。

    什么时候回收

    对象太多,空间不够了,优先从年轻代里的对象开始回收

    回收哪些对象

    通过可达性分析算法来筛选回收目标,追踪引用链的根对象是否属于GCRoots.
    还要进一步通过引用类型来判断。

    GCRoots:

    • 线程栈中的局部变量
    • 方法区的静态变量

    引用类型

    有GCRoots引用就不回收么?不是的,还要看引用的类型,java中引用的类型分为以下几种:

    • 强引用
      强引用 不回收
    • 软引用
      new SoftReference<T>(new T());正常回收后,空间还不够,就回收软引用的对象(可达)。
    • 弱引用
      跟无引用情况类似
    • 虚引用
      跟无引用一样吧?少见。

    最后的希望

    回收前,调用对象的finialize方法,若 重写Object的finialize()方法并把自己的引用关联到GCRoots上,那么久不回收了。


    年轻代GC

    1.算法演进

    1.1 标记清除

    会产生内存空间碎片,不连续的空闲空间。

    1.2 复制算法

    把剩下的对象拷贝到另一块内存空间,连续排列,避免碎片。

    • 内存空间一分为二,同时只使用其中一个
    • 空间比较浪费,同时只使用其中一个
    • 存活的对象比较小,占用一半空间太浪费

    1.3 复制算法优化

    1个Eden +2个Survivor ,通常空间比例为:8:1:1

    • 新对象占比高,给与较大的空间,使用Eden。
    • 存活的对象占比少,给与较小的空间,使用Survivor。
    • Eden和suvivor中存活的对象,复制到另外一个Survivor中。
    • 只闲置一个Survivor的空闲,空间浪费少

    Minor GC

    Minor GC的执行时机

    简单理解MinorGC :Eden满了,触发MinorGC,把两个内存区域中的存活对象都迁移到另一个Survivor中。

    1. 默认经历15次MinorGC,存活对象进入老年代

    这个具体是多少岁进入老年代,可以通过JVM参数“-XX:MaxTenuringThreshold”来设置,默认是15岁。

    2. 动态对象年龄判断

    当前 Survivor区域中,年龄1+年龄2+年龄n的多个年龄段的对象大小总和超过了Survivor区域的50%(阈值可调),此时会把年龄n以上的对象都放入老年代。

    注意:
    若要避免此种情况,

    1. 增加Survivor大小
    2. 若新生代内存有限,那么可以调整"-XX:SurvivorRatio=8"这个参数,默认是说Eden区比例为80%,也可以降低Eden区的比例,给两块Survivor区更多的内存空间,然后让每次Minor GC后的对象进入Survivor区中,还可以避免动态年龄判定规则直接把他们升入老年代。

    3. 大对象直接进入老年代

    有一个JVM参数,就是“-XX:PretenureSizeThreshold”,可以把他的值设置为字节数,比如“1048576”字节,就是1MB。
    如果新创建的对象大小超过了这个阈值,则直接放置到老年代里去。

    思考:如果要避免一次性大对象出现在Old GC中,考虑增加年轻代大小。

    4. Minor GC后,对象太多Survivor放不下

    这些对象直接转移到老年代。

    注意:
    若这些对象也将很快就释放,考虑增加Survivor区域大小。

    5. 老年代空间分配担保规则 ,Minor GC前的检查

    问题,如果老年代空间不足时,怎么处理?

    Minor GC前,会判断老年代可用空间大小 是否大于 新生代所有对象总大小,即老年代空间够不够。

    1. 空间足够:立即执行MinorGC;此时,即使Survivor空间不够,需要直接转移到老年代也是可以的。
    2. 空间不足:
      2.1 看老年代的内存大小,是否大于之前每一次Minor GC后进入老年代的对象的平均大小。
      2.1.1 老年代剩余大小 > 历次Minor GC的平均大小,则触发Minor GC
      2.1.2 老年代剩余大小 < 历次Minor GC的平均大小, 则触发 Full GC,释放老年代空间后,执行Minor GC

    留意:在JDK 6 Update 24之后,HandlePromotionFailure参数不会再影响到虚拟机的空间分配担保策略

    Minor GC的结果
    1. Minor GC后,都能放入Survivor中
    2. Minor GC后,Survivor中放不下,老年代能放下,直接放入老年代中
    3. Minor GC过后,剩余的存活对象的大小,大于了Survivor区域的大小,也大于了老年代可用内存的大小。此时老年代都放不下这些存活对象了,这个时候就会触发一次“Full GC”。

    Full GC

    场景:

    • Minor GC前
      检查老年代空间,若检查失败(不足以放下存活的新生代对象),则执行Full GC,然后执行Minor GC
    • Mino GC后,老年代的空间不足以存放 存活的新生代对象,则执行Full GC

    Full GC后的结果:

    1. Full GC过后,老年代空间足够支撑MinorGC回收的年轻代对象,(如果是MinorGC前检测老年代空间不够引发的Full GC,则之后会接着执行Minor GC).
    2. 老年代还是没有足够的空间存放Minor GC过后的剩余存活对象,那么会触发OOM

    老年代GC算法

    标记整理算法

    把剩下的数据布局在连续的空间中,避免内存碎片
    对比 “标记清除”算法,虽然会避免空间碎片,但是整理到引起更多的内存拷贝。
    老年代算法比新生代的慢很多,可能是10倍,所以Full GC会出现系统卡顿。

    垃圾回收器
    • Serial和Serial Old垃圾回收器
      1. 分别是新生代和老年代的 回收工作。
      2. 单线程工作,性能不好
    • ParNew和CMS垃圾回收器:
      1. 一般分别用于新生代和老年代的回收工作
      2. 多线程并发处理,性能好,一般是线上生产系统的标配组合
    • G1垃圾回收器:
      统一收集新生代和老年代,采用更加优秀的算法和设计机制

    Major GC和Full GC的区别是什么?触发条件呢?

    关键业务系统的JVM参数推荐(2016热冬版)

    相关文章

      网友评论

          本文标题:GC概要串烧

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