2023-08-10  阅读(48)
原文作者:Ressmix 原文地址:https://www.tpvlog.com/article/85

一、简介

上一章中,我们通过一个实际案例讲解了如何进行新生代的JVM参数调优,本章我们来继续分析这个问题,在上一章优化好的背景下,讲解如何进行老年代的调优。

我们先来回顾下,每隔20s,新生代经历Minor GC后,会产生100MB存活对象:

202308102128179631.png

JVM的参数配置如下:

-Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=5 -XX:PretenureSizeThreshold=1M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC

二、老年代调优

2.1 何时进入老年代

晋升年龄

首先,由于我们设置的晋升年龄为5,所以躲过5次Minor GC的存活对象会进入老年代,这些对象一般是系统的业务逻辑组件,不会很大,基本也就几十MB,它们会长期存活在老年代中:

202308102128185562.png

大对象

按照我们的JVM参数,超过1MB的大对象会直接进入老年代。但是我们的系统中假设是不存在这种大对象的,所以可以忽略这块内容。

Survivor空间不足

Minor GC过后,存活对象大小可能超过Suvivor空间大小(200MB),虽然我们之前已经对此进行了优化,调整过S区的大小,但是在一些突发场景下,比如大促期间,仍然可能出现超过200MB的存活对象。

我们现在就假设每隔5min,就会有一批超过200MB的存活对象进入老年代:

202308102128192293.png

那么多久会触发Full GC呢?之前分析过,触发Full GC一共有以下几种情况(排除JDK1.6及以前的情况):

  1. 老年代可用内存 < 历代晋升老年代的平均对象大小;
  2. 老年代可用内存 < 本次晋升老年代的存活对象大小;
  3. 设置了-XX:CMSInitiatingOccupancyFaction参数,当老年代内存占用达到该比例时,也会触发Full GC;

其实在生产环境下,只要对新生代进行过上一章节的优化,那对象进入老年代的速度是非常慢的。很可能在系统运行了大约半小时~1小时之后,才会有接近1G的对象进入老年代,而且上述Full GC的情况一般需要在老年代近乎快占满的情况下才可能触发。

Concurrent Mode Failure

经过前面的推算,我们基本可以知道,系统运行1小时后,老年代的存活对象大概有900MB,此时就会触发一次Full GC:

202308102128199074.png

但是由于老年代只剩下约100MB的可用空间,所以在CMS进行并发清理的时候,如果有新的对象(假设大约200MB)进入老年代,就会出现“Concurrent Mode Failure”:

202308102128205305.png

当出现“Concurrent Mode Failure”时,JVM会立即进入“Stop the World”,然后切换成Serial Old垃圾回收器,采用单线程方式进行老年代的垃圾回收,回收掉900MB对象后,再恢复系统运行:

202308102128213496.png

Concurrent Mode Failure出现的概率其实是非常小的,首先必须是在CMS触发Full GC期间,其次还需要在此期间有对象晋升到老年代,且这些对象的大小要大于老年代可用空间。

经过上述调优后,JVM的参数配置如下:

-Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=5 -XX:PretenureSizeThreshold=1M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFaction=92

2.2 内存碎片整理

在CMS完成Full GC后,会对老年代进行内存碎片整理。我们可以通过配置来设置经过多少次Full GC后进行一次内存碎片整理。

但是,通过上述分析,我们知道,Full GC的频率本身并不高,在高峰时期也就一个多小时一次,高峰过去后,很可能几小时才会触发一次Full GC。所以,对于内存碎片整理,保持默认值就可以,即每次Full GC完成后进行一次整理。

经过上述调优后,JVM的参数配置如下:

-Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=5 -XX:PretenureSizeThreshold=1M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFaction=92 -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0

三、总结

本章,我们讲解了针对老年代的调优,其实可以看到,老年代调优的根本目的就是降低Full GC频次,而前提就是对Minor GC进行优化,以减少对象进入老年代。

对于很多Java系统而言,只要对系统运行期间的内存模型做好预估,然后合理分配JVM的各内存区域,尽量让Minor GC后的存活对象留在Survivor不要去老年代,那么即使其余的JVM参数不做优化,系统性能基本上也能满足要求。


Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文