18-动态对象年龄判断+空间分配担保规则+老年代回收算法

动态对象年龄判断

本文中用到的案例是接着上一篇文章继续的,如果有不清楚同学请先查看上一篇文章

为了能更好地适应不同程序的内存状况,HotSpot虚拟机并不是永远要求对象的年龄必须达到- XX:MaxTenuringThreshold才能晋升老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到-XX:MaxTenuringThreshold中要求的年龄。

我们来看执行后的内存情况:

Heap
 def new generation   total 9216K, used 4316K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  52% used [0x00000000fec00000, 0x00000000ff037008, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff4002d8, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 tenured generation   total 10240K, used 4949K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  48% used [0x00000000ff600000, 0x00000000ffad5400, 0x00000000ffad5400, 0x0000000100000000)
 Metaspace       used 3265K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 354K, capacity 388K, committed 512K, reserved 1048576K

老年代占比有48%,比预期只有allocation2对象占比40%多出了8%,那么也就是说allocation1和allocation2对象都直接进入了老年代,并没有等到15岁的临界年龄。因为这两个对象加起来已经达到了4.25MB, 并且它们是同年龄的,满足同年对象达到Survivor空间一半的规则。

我们来通过以下案例代码来说明巩固:

    private static final int _1MB = 1024 * 1024;

    public static void testTenuringThreshold() {
        byte[] allocation1, allocation2, allocation3,allocation4;
        allocation1 = new byte[_1MB / 4]; // allocation1+allocation2大于survivo空间一半
        allocation2 = new byte[_1MB / 4];
        allocation3 = new byte[4 * _1MB];
        allocation4 = new byte[4 * _1MB];
        allocation4 = null;
        allocation4 = new byte[4 * _1MB];
    }

allocation1对象和allocation2对象以及allocation3对象都可以存放进Eden区,当allocation4对象申请分配的时候空间不足,这时进行第一次GC回收:(这里不再体现系统的一些对象占用)

image

allocation1对象和allocation2对象进入s1区,大对象allocation3直接进入老年代:

image

接着执行最后两段代码:

allocation4 = null;
allocation4 = new byte[4 * _1MB];

触发第二次GC,但是由于a1+a2这两个对象加起来已经到达了512KB,并且它们是同年龄的,满足同年对象达到Survivor空间一半的规则。根据动态年龄判断规则,这时直接进入老年代:

image

空间分配担保

之前我们讲过,如果Eden区中的对象无法存入Survivor区则会通过空间分配担保,让对象直接进入老年代。

But!大家是否想过一个问题:如果老年代里空间也不够这些对象呢?又该咋整!别急,我们一步一图继续讲解。

老年代空间够用

首先:在发生Minor GC之前,虚拟机必须先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那这一次Minor GC可以确保是安全的。

试想一个极端情况就是MinorGC后所有对象存活下来,那所有的对象都会进入老年代,如果老年代判断剩余空间是大于所有对象的那么就可以放心担保进入老年代

老年代空间不够

但是:假如执行Minor GC之前,发现老年代的可用内存空间已经小于新生代的全部对象大小了,那么这个时候就有可能新生代Minor GC后对象全部存活,然后需要转移到老年代,但是老年代空间又不够的情况。(理论上是有这种可能得)因此JVM在Minor GC之前,当判断到老年代的可用内存已经小于新生代的全部对象大小,会看一个参数:“-XX:HandlePromotionFailure”是否设置了。如果有该参数的设置,那会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小。当判断到历次平均大小是小于老年代可用内存空间的,将尝试进行一次Minor GC,尽管这次Minor GC是有风险的;如果小于,或者-XX:HandlePromotionFailure没有设置,那这时就要改为进行一次Full GC。

举个栗子,之前每次Minor GC之后,平均都有10MB左右的对象会进入老年代,那么此时老年代可用内存大于10MB,这就说明,很可能这次Minor GC过后也是差不多10MB左右的对象会进入老年代,此时老年代空间是够的。

取历史平均值来比较其实仍然是一种赌概率的解决办法,也就是说假如某次Minor GC存活后的对象突增,远远高于历史平均值的话,依然会导致担保失败。如果出现了担保失败,那就只好老老实实地重新发起一次Full GC,这样停顿时间就很长了。虽然担保失败时绕的圈子是最大的,但通常情况下都还是会将-XX:HandlePromotionFailure开关打开,避免Full GC过于频繁。

我们通过完整的一张流程图来帮助大家更好的梳理清楚整个JVM的空间担保原则:

image

小结: 通过以上的分析我们其实也知道了,老年代触发垃圾回收的时机,一般就是两个:

  1. Minor GC之前发现要进入老年代的对象太多,装不下,触发Fu'll GC 再带着进行Minor GC
  2. Mionr GC过后,剩余对象太多老年代存放不下,触发Full GC

老年代垃圾回收算法-标记整理算法

那么对于老年代的垃圾回收采用的是什么算法呢?

标记-复制算法在对象存活率较高时就要进行较多的复制操作,效率将会降低。更关键的是,如果 不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存 活的极端情况,所以在老年代一般不能直接选用这种算法。

针对老年代对象的存亡特征,1974年Edward Lueders提出了另外一种有针对性的“标记-整 理”(Mark-Compact)算法,其中的标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向内存空间一端移动,然后直接清理掉边界以外的内存,“标记-整理”算法的示意图如下图所示。

image

标记-清除算法与标记-整理算法的本质差异在于前者是一种非移动式的回收算法,而后者是移动式的。是否移动回收后的存活对象是一项优缺点并存的风险决策:

如果移动存活对象,尤其是在老年代这种每次回收都有大量对象存活区域,移动存活对象并更新 所有引用这些对象的地方将会是一种极为负重的操作,而且这种对象移动操作必须全程暂停用户应用 程序才能进行,这就更加让使用者不得不小心翼翼地权衡其弊端了,像这样的停顿被最初的虚拟机 设计者形象地描述为“Stop The World”。

老年代的垃圾回收算法速度至少比新声代的垃圾回收算法的速度慢10倍!如果频繁出现老年代的Full GC,会导致系统性能被严重影响,出现频繁卡顿的情况!

所有后面用各种案例给大家展现出来的就是在各种业务系统的生产故障下,如何去一步一步分析为什么会频繁触发Full GC,然后怎么通过调整JVM的参数来进行优化!

如果大家透彻的理解了最近几篇文章涵盖的JVM运行原理,就应该能明白,所谓 JVM 优化就是尽可能的让对象都在新生代分配和回收,尽量避免频繁的老年代Full GC ,同时给系统充足的内存大小,避免新生代也频繁的垃圾回收,更好的保证系统的运行效率。

关于如何优化JVM,后续会有大量的案例带着大家去实战。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容