一次应用多次fgc原因的排查及解决

news2025/7/19 15:42:30

应用多次fgc性能排查(一次抢购引起的性能问题)

大家好我是魔性的茶叶,今天分享一个项目jvm多次fgc的整个排查流程

上班后不久运维突然通知我们组,有一个应用在短时间内多次fgc,即将处于挂掉的状态。

首先我登录skywalking,观察应用整体状况

首先先交代下背景,该项目属于一个整合消息的项目,可以称之为消息中心,负责我们应用中的所有的推送,短信及公众号推送,消息都是通过业务方面投递到kakfa消费的方式,所以feign或者http调用的外部调用几乎没有

首先通过cpm(call per minute/每分钟服务被调用数)观察服务整体情况

我们可以看到在8.到8.07分这段时间有一个波峰,询问产品得知是有医院开启了抢购药品的业务(某些专科医院比如说皮肤病医院,会在一些特定时段,每天开启限制数量的药品抢购,这个抢购属于比较火爆的业务),所以这个流量峰值是正常的。流量一大,消息队列中的消息数量立刻就上来了:

那么我第一个想法,有没有可能是kafka消息堆积呢?应用的处理能力跟不上,导致消息大量堆积,带着这个问题,我去看一下skywalking监控的消费速度,打开Endpoint监控,观察slow endpoints:

可以看到最慢的kafka消费速率在234ms,对于一个需要调用第三方外部接口完成业务的消费来说,这个速度不可以说慢,甚至还可以说有点小快。没关系,只要是性能问题,那么总是有迹可循。我点开最高负载的服务实例查看gc次数:

可以看到在8.6分流量最高的这段时间fgc的次数达到了70次,这确实离谱,按照我的经验一个健康的应用甚至不应该一天fgc超过10次,这一分钟超过70次居然还没当场挂掉,简直可以称之为坚挺。我们再看看jvm线程数量:

waiting的线程在流量冲入后大量增加,几乎导致了oom,在流量波峰过去后线程数量又慢慢归于平稳

所以观察结论是大量被创建的线程导致的内存飙高,接下来就是需要观察线程快照找出罪魁祸首了

将线程堆栈导入fastthread.io/分析堆栈,我们查看线程数最多的相同线程组

这个明显是OkHttp使用不当导致的OkHttp链接池的异常增多,我第一个反应是联想到我们的fegin调用,feign底层可能使用了OkHttp导致OKHttp链接池创建异常。下一秒就把自己的推测推翻了,因为首先feign不可能有这么明显的问题,第二是上图中的“OkHttp api.tpns.tencent.com”我们并不是通过feign调用,而是直接通过sdk调用的(这个是tpns,腾讯的app推送)

那么,有没有可能是sdk的问题呢?

我们是这么调用腾讯的推送的:

//每次推送都会调用这段代码 
XingeApp xingeApp = new XingeApp.Builder()
                .appId(androidAppId)
                .secretKey(androidSecret)
                .domainUrl(PREFIXURL)
                .build();
 return xingeApp.pushApp(pushAppRequest);
复制代码

点开build方法发现是这样的

 public XingeApp build() {
            if (appId == null || secretKey == null) {
                throw new IllegalArgumentException("Please set appId and secret key.");
            }
​
            return new XingeApp(this);
        }
​
​
private XingeApp(Builder builder){
        if(builder.domainUrl != null){
            restapiV3.setDomainUrl(builder.domainUrl);
        }
​
        this.accessId = builder.appId;
        this.secretKey = builder.secretKey;
        this.isSignAuth = builder.useSignAuth;
​
        client = new OkHttpClient.Builder()
                .proxy(builder.proxy)
                .connectTimeout(builder.connectTimeOut, TimeUnit.SECONDS)//设置连接超时时间
                .readTimeout(builder.readTimeOut, TimeUnit.SECONDS)//设置读取超时时间
                .build();
 }
​
​
复制代码

可以看到这个new XingeApp(this)到后面是初始化了一个OkHttpClient的客户端,点进OkHttpClient的Builder方法是这样的:

public Builder() {
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;
      connectionSpecs = DEFAULT_CONNECTION_SPECS;
      eventListenerFactory = EventListener.factory(EventListener.NONE);
      proxySelector = ProxySelector.getDefault();
      cookieJar = CookieJar.NO_COOKIES;
      socketFactory = SocketFactory.getDefault();
      hostnameVerifier = OkHostnameVerifier.INSTANCE;
      certificatePinner = CertificatePinner.DEFAULT;
      proxyAuthenticator = Authenticator.NONE;
      authenticator = Authenticator.NONE;
    //可以看到罪魁祸首就在这里
      connectionPool = new ConnectionPool();
      dns = Dns.SYSTEM;
      followSslRedirects = true;
      followRedirects = true;
      retryOnConnectionFailure = true;
      connectTimeout = 10_000;
      readTimeout = 10_000;
      writeTimeout = 10_000;
      pingInterval = 0;
    }
复制代码

于是真相大白,原来ConnectionPool对象是在这里new出来的

可以看到,每次new一个XingeApp就会new一个OkHttpClient,顺便new一个ConnectionPool。而每次推送的都会new一个XingeApp导致内存中有大量的ConnectionPool对象存在,直到堆满了进行一次fgc,又能回收掉很大部分的内存,因为大部分ConnectionPool在推送完成后以后都是没用的垃圾内存,推送又是海量的,所以导致内存一直满一直fgc但是又能一直抗住(因为每次fgc可以gc掉大部分内存)

那么怎么解决呢?

最简单的解决方式就是减少XingeApp实例的对象,因为没必要每次推送都去new一个对象出来,按理说只需要一个实例就够了,OkHttpConnectionPool也只需要一个,链接的复用和摧毁完全交给OkHttp就行。所以修改如下:

//伪代码 双重校验锁
public static XingeApp getAndroidXingeAppInstance(){
        if (androidXingeAppInstance !=null){
            return androidXingeAppInstance;
        }
        synchronized (TpnsPushLogicImpl.class) {
            if (androidXingeAppInstance !=null) {
                return androidXingeAppInstance;
            }
            androidXingeAppInstance = new XingeApp.Builder()
                    .appId(staticAndroidAppId)
                    .secretKey(staticAndroidSecret)
                    .domainUrl(PREFIXURL)
                    .build();
            return androidXingeAppInstance;
        }
}
​
public void pushMessage(){
     XingeApp xingeApp = getAndroidXingeAppInstance();
    xingeApp.pushMessage;
}
​
复制代码

升级后在推送高峰期重新查看skywalking仪表板:

可以明显看到线程数量降下来了,gc也只有ygc,说明很健康

重新打出该应用的线程快照,导入线程快照分析工具查看okHttpConnectionPool线程

可以看到现在应用内只有一条OkHttpConnection的线程,改造成功

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/38154.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

客户听不进去,很强势,太难沟通了,怎么办?

案例 最近接手一项目,项目范围蔓延,成本超支,进度延期,问项目经理怎么回事? 项目经理C无奈诉苦到:用户A大领导,是业主B的领导,咱们业主B不敢反驳他,让我直接与用户A对接,说A提的需求,默认答应,我有什么办法啊,只能接了。 分析 经过复盘,导致项目蔓延的主要原因…

【参赛经历总结】第五届“传智杯”全国大学生计算机大赛(初赛B组)

成绩 比赛链接 比赛过程 被虐4h 比赛体验不是很好我开始五分后才在qq上看到题第一题签到题第二题调了1h吧,算法题做的不多,题目没读全,wa了几发,有几发是网络问题,交了显示失败,但还是判wa了第三题知道…

mulesoft What‘s the typeOf(payload) of Database Select

Whats the typeOf payload of Database SelectQuestionOptionExplanationMule ApplicationDebugQuestion Refer to the exhibit. The Database Select operation returns five rows from a database. What is logged by the Logger component? Option A “Array” B “Objec…

第五届传智杯-初赛【B组-题解】

A题 题目背景 在宇宙射线的轰击下,莲子电脑里的一些她自己预定义的函数被损坏了。 对于一名理科生来说,各种软件在学习和研究中是非常重要的。为了尽快恢复她电脑上的软件的正常使用,她需要尽快地重新编写这么一些函数。 你只需输出fun(a,…

数据库错误知识集3(摘)

(摘) 逻辑独立性是外模式不变,模式改变时,如增加新的关系,新的属性,改变属性的数据类型,由数据库管理员对各个外模式/模式的映像做相应改变,可以使得外模式不变,因为应用…

sdfsdfasfsdfdsfasfdfasfasadsfasdfasf

白包api 图片编辑功能: 1、你变体下 SnapEditMenuManager 的 这个方法 public void initEditMainMenu(final EditUITabMenu editUITabMenu) 换成你需要的配置,需要哪个功能 就拿哪一坨,别拿多了 //相框 MenuBean menuBean new MenuBean…

线程的学习

v# 1. 线程基本概念 1.1进程 进程是指运行中的程序,比如启动了QQ,就相当于启动了一个进程,操作系统会为该进程分配空间;当我们使用迅雷,就相当于又启动了一个进程,操作系统将为迅雷分配新的内存空间&…

坦克大战①

1. java绘图技术 JFrame:画板 Jpanel:画板 Graphics:画笔 初始化画板,定义画框的大小,设置可视化; 1.1 画坦克 初始化我方坦克、敌方坦克 绘图:(1)更改背景颜色;(2)绘制敌方坦克…

【Docker】常用命令

背景 当下,docker技术已成为开发者常用的技术栈。不管是开发过程中需要应对的各种复杂多变的开发环境的搭建,还是生产部署环节需要的自动化运维,都离不开docker,本文简单介绍相关命令的含义,用作平时查询使用。 1. doc…

【计算机毕业设计】38.网上轰趴预订系统

一、系统截图(需要演示视频可以私聊) 摘要 在网上轰趴发展的整个过程中,网上轰趴预定担负着最重要的角色。为满足如今日益复杂的管理需求,各类网上轰趴程序也在不断改进。本课题所设计的网上轰趴预定系统,使用SSM框架…

一文看懂Transformer(详解)

文章目录Transformer前言网络结构图:EncoderInput EmbeddingPositional Encoderself-attentionPadding maskAdd & NormFeed ForwardDecoderinputmasked Multi-Head Attentiontest时的Decoder预测Transformer 前言 Transformer最初是用于nlp领域的翻译任务。 …

大屏图表,ECharts 从“熟练”到入门

📖阅读本文,你将 了解 配置驱动 的思想理解 Echarts 基本概念了解 graphic 和 动画基本玩法。了解 Echarts 基底组件的封装的思路 一、不是标题党!Echarts,简历上人均 “熟练”? 公司最近在招外包,而因为…

基于ASP学生资助管理系统的设计与实现

项目描述 临近学期结束,还是毕业设计,你还在做ASP程序网络编程,期末作业,老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下,你想解决的问题…

用Python蹭别人家图片接口,做一个【免费图床】吧

打开本文,相信你确实需要一个免费且稳定的图床,这篇博客就让你实现。 文章目录⛳️ 谁家的图床⛳️ 实战编码⛳️ 第一轮编码⛳️ 第二轮编码⛳️ 第三轮编码⛳️ 第四轮编码⛳️ 谁家的图床 这次咱们用新浪微博来实现【免费图床应用】,通过…

栈浅谈(上)

目录 栈的定义 栈的实现 初始化 判断栈是否为空 入栈操作 获取栈顶元素 出栈操作 遍历栈 销毁栈 完整代码演示 栈—STL 基本操作 例题 参考代码 栈的定义 说到栈,一些不会计算机语言的“小白”(我就是)就会想到栈道之类的词语…

基于JavaWeb的婚恋交友网站设计与实现

项目描述 临近学期结束,还是毕业设计,你还在做java程序网络编程,期末作业,老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下,你想解决的问…

如何平衡新老策略的好与坏,一道常见风控送命题解答

作为一个风控策略从业者,在做风险管理的过程中一直在做的就是,不断的挖掘有效的变量特征来填充风控决策体系,使决策体系的功效变大变强,最终形成一套可变的稳定风险护盾。我们常见的一个场景比如如何筛选一些新策略来挑战老策略&a…

【C++中预处理语句 include、define、if】

1.预处理阶段 预处理阶段,在实际发生编译之前就根据对应的预处理语句进行操作,等到预处理阶段完成之后才进行编译阶段 。 2.预处理语句 预处理语句主要有include、define、if 和 program。利用 # 进行标记 2.1 include语句 include语句就是将所包含的…

if-else练习

if单分支 输入两个数&#xff0c;分别放入x和y中&#xff0c;若两数不相等&#xff0c;则输出其中的大数&#xff0c;若两数相等&#xff0c;则输出字符串“xy&#xff1a;”并输出其值 #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() {int a 0;int …

智慧油田解决方案-最新全套文件

智慧油田解决方案-最新全套文件一、建设背景1、智慧油田的概念及意义2、智慧油田的建设目标二、建设思路三、建设方案四、获取 - 智慧油田全套最新解决方案合集一、建设背景 1、智慧油田的概念及意义 石油产量、采收率、安全生产等都与石油工业未来息息相关&#xff0c;随着石…