JVM出现连续的FullGC该怎么办?

本文深入探讨了连续FullGC现象,分析其原因主要为JVM内存不足,导致应用性能下降。文章提供了增加JVM堆内存、调整Perm或Metaspace大小及部署更多JVM实例等解决方案,并强调了在生产环境应用前进行充分测试的重要性。

前言

之前已经介绍了下使用GCeasy来通过图形化界面的形式分析GC日志,下面将转载一篇写的比较好的关于FullGC的文章,介绍了线上JVM连续出现FullGC的场景以及解决方案。

转载文章

FullGC是垃圾回收过程中一个非常重要的事件。当发生FullGC的时候,垃圾收集器为了回收更多的垃圾对象,它会从JVM的所有内存中回收垃圾(Young、Old、Perm、Metaspace)。FullGC有很多个步骤,有些步骤需要暂停所有的应用线程,在这个过程中JVM是无法处理用户的请求的。JVM会使用所有的CPU来做垃圾回收,因此CPU的利用率会非常高。所以一般是非常不希望应用中出现FullGC,更别说是连续的FullGC了。连续的FullGC会导致以下问题:
1.CPU利用率飙升
2.由于应用暂停导致应用的响应时间变长,这会影响服务的可用性和用户体验。
本文就来探讨下连续的FullGC相关的话题,什么是FullGC?是如何引起的?如何来解决它。

什么是连续的FullGC?
通过例子来说下会比较简单,首先来看一个经历了FullGC的真实应用的GC日志。下面是由GCeasy分析日志得出的GC堆使用图:注意图中的高亮部分,可以看到发生了连续的FullGC(图中红色的小三角就是FullGC),如果出现了FullGC说明应用出问题了。
在这里插入图片描述
从下面回收字节数的图中可以看出来,即使是经过了FullGC,JVM还是没有回收足够多的内存。因为回收的字节数非常少,可能是大部分对象都是正在使用的活跃对象,因此无法被JVM回收。
在这里插入图片描述

什么原因导致了连续的FullGC?

导致连续的FullGC主要只有一个原因:JVM的堆内存不够用或者是Perm、Metaspace内存不够用,说明应用需要比你分配的内存更多的内存。通俗地说就是小马拉大车。所以JVM必须要努力的去清理垃圾来给存活的对象分配空间。

你可能会想,我的应用已经好好的跑了很长时间了,为什么忽然就发生连续的FullGC了?这是个好问题,可能是因为以下原因:

1.可能是自从上次调优JVM的内存以后,应用的流量忽然变大了!可能是业务量增加了,用户数量变多了。

2.在业务峰值期间应用创建了比平时更多的对象,可能你并没有对峰值时候的应用内存做调优,或者是说应用峰值时候的流量变大了。

如何来解决连续的FullGC?

主要有以下几个方法:

1.增加JVM的堆内存。
因为连续的FullGC主要是由于内存不足引起的,增加JVM的堆内存可以解决这个问题。比如之前给应用分配的是2.5G的堆内存,现在增加到3G看能否解决问题,通过给JVM传递-Xmx参数可以设置JVM的最大堆内存。-Xmx3G就设置了JVM的最大堆内存是3G。如果还是没解决问题,一点点的继续增加JVM的堆内存。不要给JVM分配过多的堆内存,因为这会增加GC的停顿时间。

2.增加Perm或者Metaspace内存
如果Perm区或者是Metaspace太小也会导致连续的FullGC,这种情况下就需要增大Perm或者是Metaspace的内存。

3.增加更多的JVM实例
另一个解决此问题的办法就是增加更多的JVM实例。当有更多的JVM实例以后,应用流量就会分摊到这些实例上,单个JVM承担的流量就降低了,那么它上面需要创建的对象随之也变少了,此时可能就不会有连续的FullGC了。

验证解决办法

如论你是用哪种办法来解决这个问题,在应用到生产环境之前一定要在测试环境做好测试,因为任何对JVM对内存设置的改变都需要进行充分的测试和验证才可以,要确保不会引起其他问题。

转载原文:
https://mp.weixin.qq.com/s/S116BZtPmLS1e1e9voEpbA
英文原文:
https://blog.gceasy.io/2016/11/22/eliminate-consecutive-full-gcs/

<think>我们正在解决JVM频繁Full GC的问题。根据引用内容,频繁Full GC通常与内存设置、GC策略、内存泄漏、代码优化等有关。我们将从原因分析到解决方案逐步展开。 引用[1]提到:频繁Full GC与内存设置不当、垃圾回收器配置不合适、内存泄漏或代码优化不足有关。 引用[2]给出了一些JVM参数调整建议,并提到使用G1收集器可能有助于减少Full GC。 引用[3]指出根本原因可能包括:请求速率过高、Minor GC频繁导致对象提前晋升、大量临时对象、线程池配置不合理等。 引用[4]则介绍了一个关于SoftReference的参数。 因此,我们将从以下几个方面进行分析和优化: 1. 内存设置与GC策略调整 2. 内存泄漏排查 3. 代码优化 4. 其他配置优化 下面详细展开:</think>### JVM 频繁 Full GC 的原因分析与优化方案 --- #### **一、核心原因分析** 根据引用内容,频繁 Full GC 的根本原因可分为四类: 1. **内存配置不当** - 堆内存分配不合理(新生代过小导致对象提前晋升老年代)[^3] - 元空间溢出:`Metaspace` 不足触发 Full GC[^2] - 大对象直接进入老年代(如未分片的超大数组) 2. **GC 策略缺陷** - 使用 CMS/Parallel GC 处理大堆时 STW 时间长[^2] - 未启用自适应调节策略(如 `-XX:+UseAdaptiveSizePolicy`) 3. **内存泄漏** - 静态集合类持有对象引用(如 `static Map`) - 未关闭的资源(数据库连接、文件流) - 线程局部变量未清理(`ThreadLocal` 滥用) 4. **代码与并发问题** - 高频创建临时对象(如日志拼接、JSON 序列化)[^3] - 线程池配置过大:线程栈总内存超过新生代容量 - 请求速率超过系统处理能力(对象创建速度 > GC 回收速度)[^3] --- #### **二、优化解决方案** ##### **1. 内存与 GC 参数调优** ```bash # 基础配置(示例:8G 堆内存应用) -Xms8g -Xmx8g # 堆内存固定避免震荡 -Xmn4g # 新生代占堆50%(推荐比例) -XX:MetaspaceSize=512m # 元空间初始大小[^2] -XX:MaxMetaspaceSize=1g # 防止元空间膨胀 -XX:+UseG1GC # G1 替代 CMS/Parallel[^2] -XX:MaxGCPauseMillis=200 # 目标停顿时间 -XX:InitiatingHeapOccupancyPercent=45 # 更早启动混合GC ``` **关键参数说明**: - **新生代比例**:新生代过小会导致 Minor GC 频繁,对象过早晋升老年代。设 $S_{\text{new}}$ 为新生代大小,$S_{\text{heap}}$ 为堆大小,建议: $$ S_{\text{new}} \geq 0.4 \times S_{\text{heap}} $$ - **G1 优势**:通过 Region 分片和增量回收,减少 Full GC 触发概率[^2] ##### **2. 内存泄漏排查** - **步骤 1**:生成内存快照 ```bash jmap -dump:live,format=b,file=heap.hprof <pid> ``` - **步骤 2**:分析支配树(MAT/Eclipse Memory Analyzer) - 查找 `java.lang.ThreadLocal` 或静态集合类 - 检查 `SoftReference` 对象存活时间(调整 `-XX:SoftRefLRUPolicyMSPerMB`[^4]) - **步骤 3**:监控对象分配 ```bash jstat -gcutil <pid> 1000 # 每1秒输出GC统计 ``` ##### **3. 代码与架构优化** - **避免临时对象**: - 用 `StringBuilder` 替代字符串拼接 - 对象池化(如 Apache Commons Pool) - **线程池改造**: ```java // 错误示范:无界队列导致内存堆积 Executors.newCachedThreadPool(); // 正确方案:限制队列长度 new ThreadPoolExecutor(core, max, 60s, new ArrayBlockingQueue<>(1000)); ``` - **请求限流**: - 当 QPS > 系统阈值时,启用熔断(如 Sentinel/Hystrix) ##### **4. 监控与应急措施** - **实时预警**: - 监控 `jvm_gc_pause_seconds_max{action="end of major GC"}` - 规则:`Full GC 频率 > 1次/分钟` 触发告警 - **应急方案**: - 临时扩容堆内存:`kill -3 <pid> && jmap -heap <pid>` - 重启时添加参数:`-XX:+ExplicitGCInvokesConcurrent`(禁止 System.gc() 触发 Full GC) --- ### 优化效果验证 1. **监控指标**: - Full GC 频率下降至 < 1次/天 - 老年代内存占用曲线平稳(无持续上升) 2. **压测验证**: - 模拟峰值流量(如 JMeter),观察 Full GC 触发条件 > **核心原则**: > - 优先解决**内存泄漏**(治本) > - 参数调优是**临时手段**(治标)[^1] > - 高并发场景需结合**限流+代码优化**[^3] --- ### 相关问题 1. 如何通过 GC 日志分析 Full GC 的具体原因? 2. G1 垃圾回收器的调优参数有哪些最佳实践? 3. 哪些工具可以实时监控 JVM 内存泄漏? 4. 如何区分 Full GC 是由内存泄漏还是流量突增引起的? 5. 在容器化环境(如 Kubernetes)中如何配置 JVM 内存参数?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值