【转帖】Java Full GC (Ergonomics) 的排查
1. Full GC (Ergonomics)
1.1 Java 进程一直进行 Full GC
例行检查线上运行的 Java 服务,通过 jstat -gcutil < pid > 命令检查 gc 情况的时候发现一个服务有点异常。可以看到以下打印的 gc 情况中,只有 FGC 的次数一直在变化,而YGC维持不变,也就是说这个服务一直在进行 Full GC,显而易见是有问题的
| S0 | S1 | E | O | M | CCS | YGC | YGCT | FGC | FGCT | GCT |
|---|---|---|---|---|---|---|---|---|---|---|
| 0.00 | 0.00 | 100.00 | 99.97 | 90.48 | 88.34 | 17498 | 91.739 | 1452 |
570.310 | 662.049 |
| 0.00 | 0.00 | 13.47 | 99.97 | 90.48 | 88.34 | 17498 | 91.739 | 1453 |
571.179 | 662.918 |
| 0.00 | 0.00 | 39.33 | 99.97 | 90.48 | 88.34 | 17498 | 91.739 | 1454 |
571.879 | 663.118 |
1.2 Full GC 的原因
检查 gc 日志,发现有以下 log,可以看到发生 Full GC 的原因是 Ergonomics,并且年老代 Full GC 前后占用的内存几乎不变。查找资料,发现当使用 Server 模式下的ParallelGC 收集器组合(Parallel Scavenge + Serial Old)时,会在 Minor GC前进行一次判断,也就是 内存空间分配担保机制:
- Eden 空间不足发生
Minor GC之前,虚拟机先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果条件成立的话,那么Minor GC可以确认是安全的。如果不成立的话虚拟机查看HandlePromotionFailure的值是否允许担保失败,如果HandlePromotionFailure的值不允许冒险,那么就要进行一次Full GC。如果允许则检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于将继续尝试进行Minor GC,小于的话就要进行Full GC以保证Minor GC的空间担保成功
这个 Java 服务没有通过启动参数配置垃圾收集器,查看堆配置发现是使用的默认 Parallel GC 。显然,合理的推测是空间分配担保失败导致了Full GC
[Full GC (Ergonomics) [PSYoungGen: 82944K->8916K(84992K)] [ParOldGen: 175101K->175101K(175104K)] 258045K->184018K(260096K), [Metaspace: 106250K->106166K(1153024K)], 0.4203767 secs] [Times: user=1.29 sys=0.00, real=0.42 secs]
1.3 检查堆占用
使用
jmap -heap 32006命令查看堆内存使用情况,发现老年代的使用已经达到了99.97697796737938%,只剩下了0.03M,是可以和之前的推测相佐证的PS Old Generation
capacity = 179306496 (171.0MB)
used = 179265216 (170.96063232421875MB)
free = 41280 (0.03936767578125MB)
99.97697796737938% used- 1
- 2
- 3
- 4
- 5
使用命令
jmap -histo 32006 | head -n 30查看堆内存中占用内存最多的前 30 个类实例,发现可疑的类有以下 3 个。其中SpringValue为携程开源框架 apollo 的内部类,LinkedListMultimap是 apollo 引用的 google 开源的集合类,像这种开源的代码如果有内存泄露的问题估计早就被曝出来修复掉了,所以唯一的疑点就是项目内部自己实现的MessageProcessor这个类了- com.ctrip.framework.apollo.spring.property.SpringValue --53万个对象,占用 23M
- com.google.common.collect.LinkedListMultimap$Node --53万个对象,占用 21M
- com.service.task.client.MessageProcessor – 53万个对象,占用 17M
2. 代码检查
检查代码,发现 MessageProcessor 类通过注解 @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)修饰,每次使用的时候都会新建一个对象,其内部还通过注解 @Value 引用了 apollo 配置。这个类的功能是从消息队列中拉取消息,然后将其分发给处理函数,从而完成一次消息处理。这个类之所以被设计成多实例,可以参考Spring 多实例注入,没错,就是笔者自己写的,因此原因也很清楚了
- 脚本每被调度一次,
MessageProcessor就创建一个新实例用于从指定的消息队列中拉消息。这样时间一长,MessageProcessor对象大量被创建,堆积在堆内存年轻代中,触发Minor GC。本来这些只使用一次的对象理应在多次 Minor GC 中慢慢被回收掉,但是 JVM 的动态年龄机制是如果在 Survivor 中相同年龄所有对象大小的总和大于 Survivor 空间的一半, 则年龄大于或等于该年龄的对象可以直接进入老年代,这样大量的MessageProcessor对象就跳过了年龄限制,直接进入老年代,导致老年代对象占用的内存居高不下。这种情况下当Minor GC触发时,由于老年代剩余内存空间不足,空间担保必然失败,就触发了Full GC (Ergonomics)
3. 解决方式
- 使用多实例注入的思路是没错的,错误在于笔者的使用方式。之前的使用方式在脚本启动的时候就会新建
MessageProcessor对象,造成大量的重复对象被创建,不仅浪费了内存,还会在一定程度上影响性能。基于此解决的方法很简单,只要通过@Bean(name = "xxxx")注解为每条队列单独配置好一个processor消息处理者,再使用@Resource(name = "xxxx")引用指定的MessageProcessor对象,避免大量相同的MessageProcessor对象被创建出来就可以了 - 使用多实例注入的实质是为了解决
MessageProcessor对象中某些属性的线程隔离问题,故也可以使用单例MessageProcessor对象,同时将需要隔离的属性存入ThreadLocal的解决方法。最后,最简单粗暴的解决方式是,将需要隔离的属性直接方法入参,这样就不需要考虑线程隔离问题了
【转帖】Java Full GC (Ergonomics) 的排查的更多相关文章
- Java程序线上故障排查
目录 一.Linux 内存和cpu 网络 磁盘 /proc文件系统 二.JVM Java堆和垃圾收集器 gc日志分析 JVMTI介绍 Attach机制 java自带工具 三.三方工具 jprofile ...
- JVM学习(4)——全面总结Java的GC算法和回收机制
俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及到的知识点总结如下: 一些JVM的跟踪参数的设置 Java堆的分配参数 -Xmx 和 –Xms 应该保持一个什么关系,可以让系统的 ...
- JVM学习(4)——全面总结Java的GC算法和回收机制---转载自http://www.cnblogs.com/kubixuesheng/p/5208647.html
俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及到的知识点总结如下: 一些JVM的跟踪参数的设置 Java堆的分配参数 -Xmx 和 –Xms 应该保持一个什么关系,可以让系统的 ...
- Java诊断利器Arthas优雅排查生产环境
前言 Arthas 是Alibaba开源的Java诊断工具.在线排查问题,无需重启:动态跟踪Java代码:实时监控JVM状态.对分秒必争的线上异常,Arthas可帮助我们快速诊断相关问题. 下载安装 ...
- java:线上问题排查常用手段(转)
出处:java:线上问题排查常用手段 一.jmap找出占用内存较大的实例 先给个示例代码: import java.util.ArrayList; import java.util.List; imp ...
- Java -verbose:gc 命令
Java -verbose:gc 中参数-verbose:gc 表示输出虚拟机中GC的详细情况. [Full GC 168K->97K(1984K), 0.0253873 secs] 解读如 ...
- Java进程CPU使用率高排查
Java进程CPU使用率高排查 生产java应用,CPU使用率一直很高,经常达到100%,通过以下步骤完美解决,分享一下.1.jps 获取Java进程的PID.2.jstack pid >> ...
- JAVA 从GC日志分析堆内存 第七节
JAVA 从GC日志分析堆内存 第七节 在上一章中,我们只设置了整个堆的内存大小.但是我们知道,堆又分为了新生代,年老代.他们之间的内存怎么分配呢?新生代又分为Eden和Survivor,他们的比 ...
- Java之GC
Java之GC GC:GC 是JVM的垃圾回收器.与C/C++不同,java程序员无需考虑太多内存分配的位置,更不用考虑内存释放的机制,java对象内存的申请和释放都有JVM托管.JVM的内存释放机制 ...
- Java的GC
垃圾收集 在探究Jvm的过程中,有两个点特别需要关注,一是:内存的使用,分配策略,而这一点是在前一篇博客已经介绍过了. 二是:内存的回收.也就是这一篇博客所要探究的关键点. 内存回收需要关注的几个点: ...
随机推荐
- GaussDB(DWS)函数不同写法引发的结果差异
本文分享自华为云社区<GaussDB(DWS)函数结果差异案例之greatest>,作者: 你是猴子请来的救兵吗. GaussDB(DWS)支持多种兼容模式,为了兼容目标数据库,各模式之间 ...
- 10个安全问题带你了解OWASP 定义的大模型应用
摘要:OWASP 的一群研究人员,总结目前大模型中可能存在的TOP10安全风险,很好的揭示了我们在大模型应用中需要防护的目标,以及如何采取相应的防护措施. 本文分享自华为云社区<OWASP 定义 ...
- AIGC的阿克琉斯之踵
摘要:现在,越来越多的企业和个人使用AIGC生成文章.图片.音乐甚至视频等内容,AIGC已经成为一种必备的工具.在游戏和原画师行业,甚至已经出现了第一批因为AI而失业的人. 本文分享自华为云社区< ...
- 带你掌握数仓的作业级监控TopSQL
摘要:目前TopSQL功能被用户广泛使用,是性能定位.劣化分析.审计回溯等重要的基石,为用户提供覆盖内存.耗时.IO.网络.空间等多方面的监控能力. 本文分享自华为云社区<GaussDB(DWS ...
- 企业研发治理转型利器:华为云发布流水线服务CodeArts Pipeline
摘要:2月27日,华为云正式发布流水线服务CodeArts Pipeline,旨在提升编排体验,开放插件平台,以及提供标准化的DevOps企业治理模型,将华为公司内的优秀研发实践赋能给伙伴和客户. 本 ...
- PanGu-Coder:函数级的代码生成模型
摘要:华为诺亚方舟实验室语音语义实验室联合华为云PaaS技术创新实验室基于PanGu-Alpha研制出了当前业界最新的模型PanGu-Coder 本文分享自华为云社区<PanGu-Coder 函 ...
- target.closest妙用:UI(click/mousemove/drag/drop)事件元素查找捷径
首先看下MDN:https://developer.mozilla.org/en-US/docs/Web/API/Element/closest 在jQuery时代,这个非常常见 $( documen ...
- 收钱吧与火山引擎VeDI合作一年后 有了哪些新变化?
更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 收钱吧正在和火山引擎数智平台(VeDI)跑出一条业务提效新通路. 相关数据显示,收钱吧的日服务人次就近 ...
- Solon2 接口开发: 实战 Gateway 模式效果
1.效果预览 网关 @Mapping("/api/v3/app/**") @Component public class ApiGateway3x extends UapiGate ...
- 【Docker】基础原理
基础原理 基础流程 Docker镜像讲解 Docker容器讲解 创建容器的两种方式 容器创建命令详解