记录一次sharingjdbc引起的fullgc频繁问题,总结一下fullgc频繁解决的方案

本文描述了一个由于SharingJDBC在执行SQL时未进行参数判断,导致大量对象创建并引发FullGC的问题。通过调整本地缓存配置及使用jmap命令,最终定位并修复了问题。

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

背景:最近做了个需求,为了优化效率所以做了本地缓存和redis缓存,这个跟实际问题没有很大关系,但在排查问题时造成了一定的干扰,导致排查时间变长了,这个问题的现象是上线后cpu急剧增高,fullgc次数比younggc次数多,而且每次fullgc回收的内存很少,fullgc频繁。
1.问题现象图:
在这里插入图片描述
在这里插入图片描述
2.分析:通过gc的情况可以看出一直在进行fullgc而没有进行younggc,这种情况猜测是有大对象执行进入了老年代,而且在老年代回收时没有被回收掉,正好代码里也使用的本地缓存,在上之前是有计算本地缓存会占多大内存的,通过计算是不可能成为大对象,但出问题了也只能试试了,下面是解决问题的思路。
2.1把本地缓存减小,上一版之后观察了半小时没出问题,以为改好了,但过了一段时间又出现了。
2.2把本地缓存都去掉,上一台服务器之后观察了一下午没出问题,此时认为终于可以了,全量之后不久出问题。
2.3到此时就很头痛了,不知道从何下手,因为服务器的内存有14G,使用jmap命令dump下来文件过大,在本地分析不了内存情况,幸好此时同事提供一个命令:jmap -histo:live pid | head -n 100 // 查询前100个内存使用过高的java类,使用此命令一看才发现真正的问题所在:
在这里插入图片描述
这个SQLIgnoreExpression说明sharingjdbc在执行sql报错了,在报错之后不停的在创建这个对象。然后再通过打印sql发现in里面有个空list。查看代码:
在这里插入图片描述
这个地方没有判空,这也算是sharingjdbc的一大坑,集合一定要判空,所以in操作都判空之后就没有问题了。
总结:通过此次事件发现jmap -dump:format=b,file=e.bin pid这个命令的局限性了,因为一般生产上的内存都挺大,dump下来本地根本就分析不了,感觉jmap -histo:live pid | head -n 100 // 查询前100个内存使用过高的java类这个命令还是挺好使的,可以快速的找个大对象。

<think>我们正在解决Java应用中频繁Full GC的问题。根据提供的引用资料,我们可以总结出以下排查步骤和解决方法: 1. 监控分析:首先需要收集GC日志和内存使用情况,分析Full GC发生的频率和内存使用趋势。 2. 关联业务:将Full GC的发生与应用程序的业务操作关联,检查是否有特定操作导致内存泄漏或大量对象创建。 3. 堆内存分析:通过生成堆转储(Heap Dump)文件,使用分析工具(如MAT, VisualVM)检查内存中的对象,找出内存泄漏点。 4. 检查JVM参数:确认JVM参数设置是否合理,特别是堆大小(-Xmx, -Xms)、年轻代大小、老年代大小以及垃圾回收器的选择。 5. 代码优化:根据分析结果,优化代码,避免内存泄漏和不必要的对象创建。 下面我们详细展开:</think>### Java应用频繁Full GC的排查与解决方法 频繁Full GC通常由内存泄漏、对象分配不合理或JVM配置不当引起,以下是系统化的排查流程: #### 排查步骤 1. **监控数据收集** - 启用GC日志:添加JVM参数 `-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log` - 使用监控工具: - `jstat -gcutil <pid> 1000` 实时查看各内存区使用率 - `jmap -histo:live <pid>` 统计存活对象分布 - Prometheus + Grafana 可视化监控堆内存趋势[^3] 2. **内存泄漏分析** - 生成堆转储文件: ```bash jmap -dump:format=b,file=heapdump.hprof <pid> # 线上慎用,可能STW ``` - 使用MAT/Eclipse Memory Analyzer分析 `heapdump.hprof`,重点关注: - 重复对象(Duplicate Classes) - 支配树(Dominator Tree)中的大对象 - GC Roots引用链(Path to GC Roots)[^4] 3. **业务关联分析** - 对照GC时间戳与业务日志,检查Full GC是否与特定操作同步发生(如报表生成、批量处理)[^3] - 示例:某电商平台在每日订单导出后触发Full GC,定位到未关闭的数据库连接池[^1] 4. **JVM参数检查** - 验证关键参数合理性: ```bash java -XX:+PrintFlagsFinal | grep -Ei "MaxHeapSize|MetaspaceSize|SurvivorRatio" ``` - 常见问题: - 新生代过小(`-XX:NewRatio` 比例失衡) - 元空间溢出(`-XX:MaxMetaspaceSize` 不足) - 未启用CMS/GC日志(`-XX:+UseConcMarkSweepGC`)[^2] #### 解决方案 1. **代码层优化** - 修复内存泄漏:及时关闭资源(数据库连接、文件流) - 减少大对象分配:避免在循环内创建大数组/集合 - 对象池化:重用高频创建的对象(如DTO) 2. **JVM调优** - 调整堆大小:根据业务负载设置 `-Xmx` 和 `-Xms` - 优化新生代:增大 `-XX:NewSize` 减少过早晋升 - 更换垃圾回收器: - G1:`-XX:+UseG1GC`(推荐大堆场景) - ZGC:`-XX:+UseZGC`(低延迟要求) 3. **紧急缓解措施** - 临时扩容:增加堆内存上限 - 定时重启:通过cron job在低峰期重启服务 - 流量降级:限制触发内存问题的业务入口 > **案例参考**:某金融系统因未压缩JSON报文导致Full GC,通过 `-XX:+UseCompressedOops` 启用指针压缩降低内存占用20%[^2]。 #### 验证手段 ```bash # 压测后检查对象分配 jmap -histo <pid> | grep 'com.yourpackage' # 对比优化前后GC暂停时间 grep "Full GC" gc.log | awk '{print $11}' ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值