• 一道面试题:JVM老年代空间担保机制


    面试问题

    昨天面试的时候,面试官问的问题:

    1. 什么是老年代空间担保机制?担保的过程是什么?
    2. 老年代空间担保机制是谁给谁担保?
    3. 为什么要有老年代空间担保机制?或者说空间担保机制的目的是什么?
    4. 如果没有老年代空间担保机制会有什么不好?
      下面我们就带着这些问题去了解一下JVM老年代空间担保机制吧。

    老年代空间担保机制

    在这里插入图片描述
    如图,在每次MinorGC之前,老年代可用空间会和新生代所有对象的总大小进行对比,如果老年代可用空间大于新生代所有对象的总大小,就可以直接进行MinorGC,因为即使MinorGC后所有对象都活着,S区放不下,也可以放到老年代中。如果老年代的可用空间小于新生代所有对象的总大小,就会进行下一个判断,判断老年代的可用空间和新生代每次MinorGC后进入老年代对象的平均大小,如果发现老年代的可用内存大于每次MinorGC后进入老年代对象的平均大小(举个例子:之前每次Minor GC后,平均都有10MB左右的对象会进入老年代,那么此时老年代可用内存大于10MB。这就说明很可能这次Minor GC过后也是差不多10MB左右的对象会进入老年代,此时老年代空间是够的),那么可以尝试进行MinorGC,但是有可能出现MinorGC后所有对象都存活,全部进去老年代,导致老年代可用空间不足,(例:老年代剩余100M,新生代中对象总大小为200M,但是每次MinorGC后平均进入老年代的对象大小为80M,但是此次MinorGC后所有对象全部存活,导致老年代可用空间不足)此时就会触发FullGC。反之,如果老年代的可用空间小于每次MinorGC后进入老年代对象的平均大小,则会触发FullGC。如果FullGC之后,老年代也没有足够的可用空间存放新生代的对象,则会出现OOM内存溢出。

    了解了这个担保流程之后,我们再来看我们的面试题:

    1. 什么是老年代空间担保机制?担保的过程是什么?
      上面文字解释的就是。
    2. 老年代空间担保机制是谁给谁担保?
      我理解的是老年代给新生代的S区做担保。
    3. 为什么要有老年代空间担保机制?或者说空间担保机制的目的是什么?
      目的:避免频繁的进行FullGC。
    4. 如果没有老年代空间担保机制会有什么不好?
      如果没有这个担保机制,就会直接执行Full GC,这样对性能的影响频次会增加。

    哪些对象会进行老年代

    • 动态对象年龄判断:当前放对象的Survivor区域里,一批对象的总大小大于了这块Survivor区域的内存大小的50%,那么此时大于等于这批对象年龄的对象,就可以直接进入老年代了。
    • 大对象直接进入老年代。
    • 15次Minor GC后还存活的对象。
    • Minor GC后的对象太多无法放入Survivor区。

    Full GC的触发时机

    • 老年代空间不足。比如大对象或者年龄超过15的对象进入老年代。
    • 空间分配担保失败。这个就是咱们今天说的这种情况。老年代可用内存小于新生代全部对象的大小,如果没开启空间担保参数,会直接触发Full GC,所以一般空间担保参数都会打开。
    • Concurrent Mode Failure 执行CMS GC的过程中同时有对象要进入老年代,而此时老年代空间不足(可能是GC过程中浮动垃圾过多导致暂时性的空间不足)便会报Concurrent Mode Failure错误并触发Full GC。这种情况下会使用Serial Old收集器,也就是发生了退化,CMS退化为serial gc,Serial Old收集器是单线程的,对GC的影响很大。concurrent mode failure产生的原因是老年代剩余的空间不够,导致了和gc线程并发执行的用户线程创建的大对象(由PretenureSizeThreshold控制新生代直接晋升老年代的对象size阀值)不能进入到老年代,只要stop the world来暂停用户线程,执行GC清理,单线程对全堆以及 metaspace 进行回收,STW 的时间会特别长,对业务系统的可用性影响比较大。可以通过设置CMSInitiatingOccupancyFraction预留合适的CMS执行时剩余的空间。
    • 老年代可用内存小于历次新生代GC后进入老年代的平均对象大小,此时会提前Full GC;但是"-XX:HandlePromotionFailure"参数,在JDK 1.6以后就被废弃了,所以现在一般都不会在生产环境里设置这个参数了。在JDK 1.6以后,只要判断"老年代可用空间"大于"新生代对象总和"或者"老年代可用空间"大于"历次Minor GC升入老年代对象的平均大小",两个条件满足一个,就可以直接进行Minor GC,不需要提前触发Full GC了。
    • 新生代Minor GC后的存活对象大于Survivor,那么就会进入老年代,此时老年代内存不足,触发Full GC。这里的不足就是判断条件后还是不足或者经过判断后进行YGC后放入老年代此时的空间不足,然后进行Full GC,就会出现频繁Full GC。达到一定情况后,就会OOM了。
    • 如果用的是CMS收集器,老年代可用内存大于历次新生代GC后进入老年代的对象平均大小,但是老年代已经使用的内存空间超过了("-XX:CMSInitiatingOccupancyFaction=92%"JDK6默认值 )这个参数指定的比例,也会自动触发Full GC。
    • 未指定老年代和新生代大小,堆伸缩时会触发Full GC。
    • 调用System.gc(),只是建议虚拟机执行Full GC,但虚拟机不一定真正去执行。
  • 相关阅读:
    cmd命令行查看mysql数据库命令
    数据结构(四) -- 递归
    解锁前端Vue3宝藏级资料 第三章 Vue Router路由器的使用
    AI与制药相结合,能否弯道超车
    SSM项目 - Online Music Player(在线音乐播放器)- 前端页面设计 - 细节狂魔
    ElementUI及ElementUI Plus Axure RP高保真交互元件库及模板库
    【STM32】STM32学习笔记-WDG看门狗(46)
    万界星空科技MES系统中的车间管理的作用
    第73期:图论-2022/7/19学习报告
    【学习笔记14】JavaScript的循坏语句
  • 原文地址:https://blog.csdn.net/weixin_44798288/article/details/128015896