记录一次生产应用启动后发生多次FullGc的解决过程

在生产发布后收到告警,发现应用触发了频繁的FullGC,分析日志确定问题源于Spring初始化时的Meta区压力。通过jmap命令检查JVM堆配置和Meta区使用情况,确认Meta区超过默认大小导致FullGC。调整JAVA_OPTS,增大Meta区大小至128m-256m,重启服务后FullGC次数显著减少,问题得到解决。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

昨天生产发布完后,收到告警短信5分钟内FullGc次数大于2次。当时一脸懵,应用上线前做过压力测试,没测出jvm fullgc问题啊。所以按照告警时间去查日志,发现基本为spring初始化动作。

后面过了半个小时也没有收到告警,基本猜测是meta区的问题

下面是定位过程。

1,首先ps -ef|grep java
cd /opt/wildfly/openjdk/openjdk-1.8.0_92/bin


2,[root@uqotsxgs1a01 bin]# ./jmap -heap 111255
Attaching to process ID 111255, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.92-b00

using parallel threads in the new generation.
using thread-local object allocation.
Concurrent Mark-Sweep GC

Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 4294967296 (4096.0MB)
NewSize = 174456832 (166.375MB)
MaxNewSize = 174456832 (166.375MB)
OldSize = 4120510464 (3929.625MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)(初始meta大小为20M)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)

3,查看实际meta大小
[root@uqotsxgs1a01 bin]# ./jstat -gccapacity 111255
NGCMN NGCMX NGC S0C S1C EC OGCMN OGCMX OGC OC MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC 
170368.0 170368.0 170368.0 17024.0 17024.0 136320.0 4023936.0 4023936.0 4023936.0 4023936.0 0.0 1202176.0 172332.0 0.0 1048576.0 19756.0 1951 7
[root@uqotsxgs1a01 bin]# ./jstat -gc 111255
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 
17024.0 17024.0 0.0 938.0 136320.0 13046.0 4023936.0 225361.8 172332.0 160840.8 19756.0 16833.0 1951 61.236 7 0.799 62.035


而 实际 meta可用为173M,实际已用160M,Fullgc次数为 7.。所以问题很明显,随着加载类的增多,我们的应用meta区远超过20m的大小,所以会触发fullgc,扩容metaspace。至此,问题已经很明确,因为jdk1.8的默认参数,导致需要扩容meta区,因此出现了fullgc。

4,修改meta区大小,重启服务
JAVA_OPTS="$JAVA_OPTS -Xms4096m -Xmx4096m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m"


5,查看修改的Fullgc次数 为 1,解决。
[root@uqotsxgs1a01 bin]# ./jstat -gc 35656
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 
17024.0 17024.0 3228.8 0.0 136320.0 100896.4 4023936.0 195764.5 159616.0 150182.8 18816.0 16251.0 84 4.365 1 0.578 4.943

### 垃圾回收(GC)过程详解 垃圾回收(Garbage Collection, GC)是 Java 中一种自动化的内存管理机制,其主要目的是释放不再被程序引用的对象所占用的内存空间。以下是关于 GC 的具体过程及其涉及的关键环节: #### 1. **GC 触发条件** GC 并不会随时运行,而是当满足某些特定条件时才会启动。这些条件包括但不限于以下几种情况[^2]: - 当堆中的可用内存不足以分配新对象时。 - 显式的系统调用 `System.gc()` 请求执行 GC(尽管这并不一定会立即触发 GC)。 #### 2. **死亡对象判断方法** 为了有效清理无用对象并释放内存,在 GC 执行之前需要先标记哪些对象是可以被回收的。常见的两种算法用于判定对象是否存活: - **引用计数法**:通过统计某个对象是否有其他对象引用它来决定该对象的状态。然而此方法存在循环引用的问题,因此现代 JVM 不再采用这种方式。 - **可达性分析法**:从一组称为“根节点”的对象出发遍历整个引用链表结构,任何不可达的对象都被视为可回收对象[^3]。 #### 3. **分代假设与分区策略** 基于大多数应用中不同生命周期长度的对象分布特性提出了所谓的“分代假说”,即新生代短命对象多于老年代长期存在的对象。据此将堆划分为以下几个部分: - 新生代 (Young Generation): 主要存储刚创建不久的新实例变量;频繁发生 Minor GC 来清除这部分区域内的废弃数据。 - 老年代 (Old Generation): 存放经过多次Minor GC 后仍然存活下来的较长时间存活对象;只有在必要情况下才进行 Major/Full GC 处理此类较大范围的数据集合。 - 方法区 / 元空间(Metaspace): 记录类元信息,默认较少参与常规意义上的垃圾清扫活动。 #### 4. **日志记录与性能优化** 每次完成一次完整的垃圾收集操作之后都会生成相应的日志条目供开发者查看和诊断潜在问题所在之处。典型的 gc log 包含如下几个方面的内容[^1]: - 时间戳表明何时进行了此次gc动作; - 针对不同类型的具体描述比如 young 或 full gc ; - 收集前后各个子域(eden,survivor0&1以及tenured generation)大小对比数值; - 整体耗时时长等等... 通过对上述各项指标深入剖析可以更好地理解当前系统的实际运作状况进而采取针对性措施提升整体效率表现[^4]. ```python import java.lang.management.ManagementFactory; public class GCDemo { public static void main(String[] args){ Runtime.getRuntime().addShutdownHook(new Thread(() ->{ System.out.println("JVM is shutting down..."); })); while(true){ new Object(); // Create objects continuously to trigger GC eventually. } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值