记一次线上jVM调优

文章目录

  • 问题描述
  • 问题分析
  • 尝试优化业务代码
  • 优化方案
  • 修改后代码
  • 补充点

问题描述

部门调整,接手一个新项目,为方便后续描述叫user-web,随后推动IT降本,要求根据实际业务量调整服务器实例数量和配置,调整前服务器配置为42实例x16核40g,降本方向主要为减少实例数和CPU数,参考的一个标准是近7日cpu的一个使用率调整到30%。调整前服务使用率为15%,通过几次调整后配置为4实例x8核12G,调整后在业务高峰期向外提供的rpc接口getUserInfo开始出现偶尔超时,该服务也只提供了一个rpc接口,中间随着服务器配置降低,接口超时数量开始稳定增多,后续增加到每天上百个,并且稳定出现,RT超过1s。

问题分析

引起接口超时原因有很多,正常getUserInfo接口调用时间都在5ms以内,rpc配置timeout是1s,峰值qps为60,首先这个问题每天都稳定出现,先排除网络抖动,最近又没有对项目做过任何代码修改,应该不是新引入的问题,唯一变化就是调整了服务器配置,考虑应该是资源不够用导致的。

观察服务运行时大盘,首先,观察最近30天整个服务的qps并没大的变化,发生超时的时间时业务高峰期,考虑是配置调整后资源过低,满足不了当前业务量。观察cpu使用率,峰值也在30%一下,应该不是cpu的影响,本身服务也确定不是计算密集型,符合认知,那就是可能是内存不够,内存不足,GC时STW单次时间过长或者频率过高,进而导致向外提供rpc超时,第一直觉是直接扩内存,直接翻倍,增加到16G,观察一天,依然有很多调用超时,并且对应超时时间短,内存占用率会有所波动,怀疑是有一些大对象,导致业务高峰期内存占用率过高,GC压力过大,GC的STW增加。

单台机器内存8G,堆内存占总内存3/4,8G,使用率在80%,堆外内存300M,代码中并没有使用堆外内存,应该不是堆外内存问题,堆内存也并没有持续升高,排除内存泄漏,进一步考虑是内存不够GC时STW单次时间过长或者GC频率过高引起的接口超时。

首先观察下使用的GC参数,使用的G1垃圾回收器,设置的最大停顿时间是200ms
在这里插入图片描述

下载gc日志,并筛选出GC时间比较长的日志,为了便于说明问题和正常服务gc日志进行了对比,:
超时接口提供方服务gc日志:
在这里插入图片描述
正常服务gc日志:
在这里插入图片描述

对比二者发现:

1、GC pause (G1 Evacuation Pause) (young)首先这是一次停顿应用程序(STOP)的年轻代垃圾回收,由to-space exhausted触发,回收后,Edgn 对象被清理从5252→0, 整个堆使用率显著降低,从 Heap: 8687.8M(9216.0M)->3048.5M(9216.0M),这是一次有效的垃圾回收。

2、正常gc耗时10ms,异常gc耗时是700ms+,相差70倍使用多线程并行回收垃圾(Parallel time)和拷贝对象到目标区域空间不足触发Full GC(Evacuation Failure)时主要的两部分耗时,其中对象拷贝有事并行垃圾回收阶段最耗时部分,这表明有很多大的存活对象要进行拷贝,这些对象在业务高峰,并大量创建,导致空间不够用。

尝试优化业务代码

观察整个服务的运行大盘,发现在发生问题的同一时间段,有个接口耗时较高,然后接口调用日志链,最终发现了出问题的代码:

 private LoadingCache<Integer, List<TVideo>> sortedShowVideoLoadingCache = Caffeine.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).maximumSize(1000)
                    .build(new CacheLoader<Integer, List<TVideo>>() {

                        @Override
                        public List<TVideo> load(@NonNull Integer albumId) throws Exception {
                            return myVideoProxy.getSortedShowVideosByAlbumId(albumId);
                        }
                    });
 
 。。。
 
 public List<TVideo> getShowVieosByAlbumId(int albumId) {
        log.info("sortedShowVideoLoadingCache.size={}", sortedShowVideoLoadingCache.estimatedSize());
        List<TVideo> videos = sortedShowVideoLoadingCache.get(albumId);
        if (songs != null) {
           return JsonUtils.readValue(JsonUtils.writeValue(videos), new TypeReference<List<TVideo>>() {
            });// 问题语句
        }
        return videos;
    }

问题语句分析:JsonUtils.readValue(JsonUtils.writeValue(videos), new TypeReference<List>() {});
问题1: 采集json序列化方式效率低,导致整个调用链路RT升高,和监控观察一一致,p99为2.5s;

问题2:创建了一遍多余对象,JsonUtils.writeValue(videos)创建了一个大的字符串,这个其实最后并没有返回,作为中间结果,依然占用了大量的空间,本省整个调用链路多次调用getShowVieosByAlbumId,创建的TVideo有一千多个,翻倍之后空间还是很可观的,同时由于使用序列化方式表抵销,导致在2.5s创建的这三个对象都无法释放。

优化方案

使用更高效序列化技术Hessian协议,将从rpc结果序列化为字节流,每次需要的时候,直接反序列化出来就是全新的对象。

修改后代码

 private LoadingCache<Integer, List<TVideo>> sortedShowVideoLoadingCache = ConanCaffeineUtil.toSerializedLoadingCache(
				 Caffeine.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).maximumSize(1000)
                    .build(new CacheLoader<Integer, List<TVideo>>() {

                        @Override
                        public List<TVideo> load(@NonNull Integer albumId) throws Exception {
                            return myVideoProxy.getSortedShowVideosByAlbumId(albumId);
                        }
                    }));
                    
                    
 public List<TVideo> getShowVieosByAlbumId(int albumId) {
       return sortedAllSongLoadingCache.get(albumId);
    }

优化后getShowVieosByAlbumId的RTp99下降到500ms,机器配置内存减少到8G,GC正常,没有再出现过超时。

补充点

1、其实刚开始是通过grafana对应监控大盘看接口调用时间,发现整个接口调用都是在50ms内,其实grafana统计是p99,本省超时接口数量又不多,所就看不到有长RT调用,最后通过报错日志trace调用链,观察每步调用时间。

2、中间调整了实例数、cpu核数和内存大小,cpu使用率过高会造成服务卡顿,内存不足,造成频繁gc和单次STW过长,都有可能引起提供接口超时,都需要考虑。

3、堆内存由jvm控制申请和释放,堆外内存操作系统使用,开发人员也可以手动申请堆外内存,但是不建议,容易因无忘记释放而造成内容泄漏。

4、G1的GC时间可控参数
5、G1的垃圾回收模式由几种,有什么区别?G1回收模型由Young GC、Mixed GC和Ful GC,

Young GC只回收年轻代区域(edgn,Survior)空间,仅年轻代空闲空间不足、老年代空间充足,Mixed GC除了回收年轻代,因为老年代因空间不够也需要回收,这两者都是小范围的,当复制空间不足(to-space exhausted),并多次尝试小范围回收空间依然不够时,可能会触发Ful GC,对整个堆的空间进行回收和整理,回收时间更长。

6、普通Young GC和由to-space exhausted引起的Young GC区别是,后者为了获得更多的空间,可能会执行多次额外的GC周期,停顿时间一般会大于设定的希望停顿时间。

7、G1垃圾回收器为什么将堆内存分为大小不同的块?一是提高收集的效率,多个回收线程在不同区域独立执行,二是根据需要灵活调整老年代和新生代的比例,三是更准确控制每次停顿的时间,根据历史垃圾回收记录,根据设置的最大停顿时间,使用预测模型,增量进行垃圾回收,四减少内存碎片化,每次收集之后,将对象移动到一个或者多个区域,留下来的都是大片连续可用的空间。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【DevOps】Elasticsearch 数据跨集群同步方案

目录 1、Elasticsearch Cross-Cluster Replication (CCR) 1.1、优点 1.2、缺点 1.3、步骤 1.4、示例 2. Logstash 或其他 ETL 工具 2.1、优点 2.2、缺点 2.3、步骤 3. Apache Kafka 或 RabbitMQ 3.1、优点 3.2、缺点 3.3、步骤 4、使用 Reindex API 进行跨集群同…

基于 Thingsboard 定制开发,国产化企业级、低代码 AIoT 物联网平台

项目源码&#xff0c;文末联系小编 01 ThingsKit 物联网平台 ThingsKit 是基于 Thingsboard 开源物联网平台二次开发&#xff0c;面向中小型企业开箱即用的低代码物联网平台&#xff0c;提供N1N&#xff08;N个终端产品1个物联网平台N个行业方案&#xff09;的产品服务矩阵&…

2024爱分析·AI Agent创新成就奖开启申报丨奖项征集

AI Agent正成为企业数字化转型的关键力量。它们不仅提升了工作效率&#xff0c;优化了客户体验&#xff0c;更是在数据分析、决策支持和自动化流程中扮演着至关重要的角色。随着技术的不断进步和应用场景的拓展&#xff0c;AI Agent正以其独特优势&#xff0c;引领企业进入一个…

我的第一个 iOS 程序:iTahDoodle

我的第一个 iOS 程序&#xff1a;iTahDoodle 我的第一个 iOS 程序&#xff1a;iTahDoodle应用功能对象图应用委托对象设置视图为按钮关联动作方法为 UITableView 对象提供数据保存并加载任务数据在模拟器上运行程序下载链接 我的第一个 iOS 程序&#xff1a;iTahDoodle 实现了…

QT——MySQL数据库联用

一、ODBC 1、ODBC简介 ODBC全称为Open Database Connectivity,是一种用于数据库操作的标准接口。要使用ODBC,首先需要安装相应的ODBC驱动程序,然后在系统中配置ODBC数据源。接着,可以通过编程语言(如C++、Java等)或者数据库工具(如SQL Server Management Studio)来连…

深度学习推理显卡设置

深度学习推理显卡设置 进入NVIDIA控制面板&#xff0c;选择 “管理3D设置”设置 "低延时模式"为 "“超高”"设置 “电源管理模式” 为 “最高性能优先” 使用锁频来获得稳定的推理 法一&#xff1a;命令行操作 以管理员身份打开CMD查看GPU核心可用频率&…

AIoT设备新一代高性能处理器瑞芯微RK3576,东胜物联RK3588等核心板定制开发

随着物联网、人工智能和嵌入式技术的不断发展&#xff0c;智能设备应用对芯片处理器的性能需求越来越高&#xff0c;以满足复杂的数据处理需求、实时性要求、复杂的算法运算和多任务处理能力。高性能的芯片可以为智能设备提供更强大的计算能力和更快速的响应速度&#xff0c;从…

通过nginx转发后应用偶发502bad gateway

序言 学习了一些东西&#xff0c;如何才是真正自己能用的呢&#xff1f;好像就是看自己的潜意识的反应&#xff0c;例如解决了一个问题&#xff0c;那么下次再碰到类似的问题&#xff0c;能直接下意识的去找到对应的信息&#xff0c;从而解决&#xff0c;而不是和第一次碰到一样…

新手如何入门Web3?

一、什么是Web3&#xff1f; Web3是指下一代互联网&#xff0c;它基于区块链技术&#xff0c;致力于将各种在线活动变得更加安全、透明和去中心化。Web3是一个广义的概念&#xff0c;涵盖了包括数字货币、去中心化应用、智能合约等在内的多个方面。它的主要特点包括去中心化、…

网络编程--网络理论基础(二)

这里写目录标题 网络通信流程mac地址、ip地址arp协议交换机路由器简介子网划分网关 路由总结 为什么ip相同的主机在与同一个互联网服务通信时不冲突公网ip对于同一个路由器下的不同设备&#xff0c;虽然ip不冲突&#xff0c;但是因为都是由路由器的公网ip转发通信&#xff0c;接…

Apache Paimon系列之:Append Table和Append Queue

Apache Paimon系列之&#xff1a;Append Table和Append Queue 一、Append Table二、Data Distribution三、自动小文件合并四、Append Queue五、压缩六、Streaming Source七、Watermark Definition八、Bounded Stream 一、Append Table 如果表没有定义主键&#xff0c;则默认为…

Vue3基础介绍

文章目录 一、简介1、简介2、性能提升3、源码升级4、拥抱TypeScript5、新特性 二、创建Vue3.0工程1、使用vue-cli创建2、使用vite创建 三、分析工程结构1、main.js2、组件中 一、简介 1、简介 2020年9月18日&#xff0c;Vue.js发布3.0版本&#xff0c;代号(One Piece)海贼王 …

怎么用AI绘画完成设计创作?

AI绘画工具为设计师提供了强大的功能和便利性&#xff0c;用AI绘画进行艺术创作能够使设计师能够更快地迭代和优化设计方案&#xff0c;提高设计效率。那么怎么用AI绘画完成设计创作? 要使用AI绘画完成设计创作&#xff0c;首先需要选择一个合适的工具。目前市场上有很多优秀的…

KVB:怎么样选择最优交易周期?

摘要 在金融交易中&#xff0c;周期的选择是影响交易成败的重要因素之一。不同的交易周期对应不同的市场环境和交易策略&#xff0c;选择合适的周期可以提高交易的成功率。本文将详细探讨交易中如何选择最优周期&#xff0c;包括短周期、中周期和长周期的特点及适用情况&#…

CVE-2023-38836(文件上传+命令执行)

简介 BoidCMS v.2.0.0 存在文件上传漏洞&#xff0c;远程攻击者可通过添加 GIF 头部绕过 MIME 类型检查&#xff0c;执行任意代码。 过程 打开靶场 对网站进行目录扫描 发现后台&#xff0c;登录弱口令账号密码 admin/password 发现文件上传位置 根据简介提示&#xff0c;…

Vue57-组件的自定义事件_解绑

给谁绑的自定义事件&#xff0c;就找谁去触发&#xff1b;给谁绑的自定义事件&#xff0c;就找谁去解绑&#xff1b; 一、解绑自定义事件 1-1、解绑一个自定义事件 到student.vue组件中去解绑。 1-2、解绑多个自定义事件 使用数组来解绑多个。 1-3、解绑所有的自定义事件 二、…

django学习入门系列之第三点《快速了解 CSS》

文章目录 CSS快速了解CSS应用方式在标签上在head标签中写到文件中问题&#xff1a;用Flask框架开发不方便 往期回顾 CSS CSS 专门用来"美化"标签 基础CSS,写简单的界面 &能看懂 &会改就行模块&#xff0c;调整和修改 快速了解 style 这种就叫css样式 &l…

aasist-bladedisc 音频反欺骗算法模型

AASIST 论文 参考ASIST: Audio Anti-Spoofing using Integrated Spectro-Temporal Graph Attention Networks https://arxiv.org/pdf/2110.01200.pdf 模型结构 aasist是一种开源的音频反欺诈的模型&#xff0c;主要的模型结构如下所示&#xff1a; 算法原理 环境配置 Dock…

Python实现逻辑回归与判别分析--西瓜数据集

数据 数据data内容如下&#xff1a; 读取数据&#xff1a; import numpy as np import pandas as pd data pd.read_excel(D:/files/data.xlsx) 将汉字转化为01变量&#xff1a; label [] for i in data[好瓜]:l np.where(i 是,1,0)label.append(int(l)) data[label] lab…

测试单选框

单选按钮&#xff1a;用于在一组互相排斥的选项中选择其中一项&#xff1b; 由一个圆圈和紧随其后的文本标题组成&#xff0c;当它被选中时&#xff0c;圆圈中就标上一个黑点。 通常将一组单选按钮放在一个组框控件中&#xff0c;在一组单选按钮中&#xff0c;第一个(Tab键顺序…