记录一次OSSClient使用不当导致的OOM排查过程

首发:公众号《赵侠客》

前言

最近线上有个比较边缘的项目出现OOM了,还好这个项目只是做一些离线的任务处理,出现OOM对线上业务没有什么影响,这里记录一下排查的过程

Dump日志查看

项目配置的主要JVM参数设置如下:

-Xmx5120m -XX:+PreserveFramePointer -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/usr/local/update/heap_trace.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/update/dump.log 

最大堆内存给了5G,并配置了记录GC日志,OOM后的内存导出,我们先看一下OOM的导出内存快照,dump.log居然有5GB,第一判断肯定是内存泄漏了。

然后看了一下heap_trace.log的GC日志,最后几次GC花了0.02秒,并且没有释放多少内存,肯定是内存泄漏了

2023-09-18T09:58:28.259+0800: 234057.213: [GC (Allocation Failure) [PSYoungGen: 438400K->7648K(441344K)] 763838K->333358K(961024K), 0.0140907 secs] [Times: user=0.04 sys=0.00, real=0.02 secs]  
2023-09-18T10:01:33.925+0800: 234242.879: [GC (Allocation Failure) [PSYoungGen: 436704K->7344K(441856K)] 762414K->333326K(961536K), 0.0134861 secs] [Times: user=0.04 sys=0.00, real=0.01 secs]  
2023-09-18T10:04:16.426+0800: 234405.380: [GC (Allocation Failure) [PSYoungGen: 437424K->8832K(441856K)] 763406K->335022K(961536K), 0.0147276 secs] [Times: user=0.05 sys=0.00, real=0.01 secs]  
2023-09-18T10:06:30.923+0800: 234539.877: [GC (Allocation Failure) [PSYoungGen: 438912K->11520K(442368K)] 765102K->338158K(962048K), 0.0202829 secs] [Times: user=0.06 sys=0.00, real=0.02 secs]  
2023-09-18T10:08:27.655+0800: 234656.609: [GC (Allocation Failure) [PSYoungGen: 442112K->12272K(442880K)] 768750K->340510K(962560K), 0.0216111 secs] [Times: user=0.06 sys=0.00, real=0.02 secs]  
2023-09-18T10:11:37.773+0800: 234846.727: [GC (Allocation Failure) [PSYoungGen: 442864K->12000K(445440K)] 771102K->340918K(965120K), 0.0243473 secs] [Times: user=0.06 sys=0.00, real=0.02 secs]  
2023-09-18T10:14:56.925+0800: 235045.879: [GC (Allocation Failure) [PSYoungGen: 443616K->8192K(445952K)] 772534K->337110K(965632K), 0.0152287 secs] [Times: user=0.04 sys=0.00, real=0.01 secs]  
2023-09-18T10:17:49.358+0800: 235218.312: [GC (Allocation Failure) [PSYoungGen: 439808K->8432K(445952K)] 768726K->337790K(965632K), 0.0151303 secs] [Times: user=0.05 sys=0.00, real=0.02 secs]  
2023-09-18T10:20:51.356+0800: 235400.310: [GC (Allocation Failure) [PSYoungGen: 441072K->8976K(446464K)] 770430K->338470K(966144K), 0.0159285 secs] [Times: user=0.05 sys=0.00, real=0.02 secs]  
2023-09-18T10:24:05.395+0800: 235594.349: [GC (Allocation Failure) [PSYoungGen: 441616K->9504K(446464K)] 771110K->339358K(966144K), 0.0219962 secs] [Times: user=0.05 sys=0.00, real=0.02 secs]  
2023-09-18T10:26:48.374+0800: 235757.328: [GC (Allocation Failure) [PSYoungGen: 443168K->11680K(446976K)] 773022K->341950K(966656K), 0.0195554 secs] [Times: user=0.05 sys=0.00, real=0.02 secs]

使用Jprofiler分析Dump文件

使用JProFiler打开Dump文件可以看到HashMap$Node居然有1GB

我们选择Node,发现有3000多万个对象:

我们选择Merged incoming references ,然后一步步的展开Node对象的引用链,最后我们发现有个OSSClient的对象引用了Node

想到这个业务大量使用了阿里云OSS上传文件,于是找到使用OSSClient的代码,代理中上传文件每次new OSSClient(),但是就算是每次new 个局部变量,也不应该会导致内存泄漏啊?

于是我了一下DefaultServiceClient的源码,我们可以看到创建OSSClient时调用了createHttpClientConnectionManager

    public DefaultServiceClient(ClientConfiguration config) {super(config);this.connectionManager = createHttpClientConnectionManager();this.httpClient = createHttpClient(this.connectionManager);RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();    

createHttpClientConnectionManager中使用了IdleConnectionReaper来管理当前连接:

    protected HttpClientConnectionManager createHttpClientConnectionManager() {SSLContext sslContext = null;if (config.isUseReaper()) {IdleConnectionReaper.setIdleConnectionTime(config.getIdleConnectionTime());IdleConnectionReaper.registerConnectionManager(connectionManager);}return connectionManager;}

IdleConnectionReaper.registerConnectionManager中我们可以看到使用了ArrayList来存所有的HTTP连接

public final class IdleConnectionReaper extends Thread {private static final int REAP_INTERVAL_MILLISECONDS = 5 * 1000;private static final ArrayList<HttpClientConnectionManager> connectionManagers = new ArrayList<HttpClientConnectionManager>();private static IdleConnectionReaper instance;private static long idleConnectionTime = 60 * 1000;private volatile boolean shuttingDown;private IdleConnectionReaper() {super("idle_connection_reaper");setDaemon(true);}public static synchronized boolean registerConnectionManager(HttpClientConnectionManager connectionManager) {if (instance == null) {instance = new IdleConnectionReaper();instance.start();}return connectionManagers.add(connectionManager);}

我们看到OSSClient提供了一个shutdown方法,new过的OSSClint如果不用了需要调用shutdown来释放连接,会从connectionManagers中移除对接的连接,好吧,确实是代码使用不当导致的OOM。

    @Overridepublic void shutdown() {IdleConnectionReaper.removeConnectionManager(this.connectionManager);this.connectionManager.shutdown();}  public static synchronized boolean removeConnectionManager(HttpClientConnectionManager connectionManager) {boolean b = connectionManagers.remove(connectionManager);if (connectionManagers.isEmpty())shutdown();return b;}

我在阿里云官网也找到同样的问题:
image.png

解决方法:

  1. 将 OSSClient 实例定义为单例模式,避免在应用中多次实例化 OSSClient
  2. 使用 OSSClient.shutdown() 方法关闭 OSSClient 实例,释放资源
  3. 使用 try-finally 块,在 finally 中调用 OSSClient.shutdown() 方法
  4. 在应用中使用 OSSClient 的过程中,确保使用完成后关闭 OSSClient 实例

本地复现

我们本地启用项目使用Jprofiler连接我们的JVM

这个功能是对外提供了一个接口,于是我们写个for循环一直请求这个接口,然后观察内存变化,跑了6分钟可用内存就变成了0

服务端也报了OOM:

解决问题

使用工厂模式重写代码:

    public static final Map<String, OSSClient> map = new ConcurrentHashMap<>();public static OSSClient getClient(String endpoint, String accessKey, String accessSecret) {if (!map.containsKey(accessKey)) {OSSClient client = new OSSClient(endpoint, accessKey, accessSecret);map.put(accessKey, client);}return map.get(accessKey);}

替换原来的代码:

  OSSClient client = AliyunUtil.getClient(endpoint, getAccessKey(), getAccessSecret());

再次测试,发现每次GC都很好释放了内存,跑了6分钟,内存使用不超过200M,完美解决了问题

总结

本文介绍了使用Jprofiler排查一次线上由于使用阿里云OSSClient不当导致的OOM过程,主要还是写代码时没有注意OSSClient需要自己手动Shutdown导致的,还好不是出现在核心业务系统中,不然后果就比较麻烦了,以后使用别人提供的工具时一定要多看看官方是如何使用,多翻翻源码,避免再出现类似的问题。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java精品项目源码第61期垃圾分类科普平台(代号V061)

Java精品项目源码第61期垃圾分类科普平台(代号V061) 大家好&#xff0c;小辰今天给大家介绍一个垃圾分类科普平台&#xff0c;演示视频公众号&#xff08;小辰哥的Java&#xff09;对号查询观看即可 文章目录 Java精品项目源码第61期垃圾分类科普平台(代号V061)难度指数&…

Word 文档转换 PDF、图片

工作有需要 Word 文档转换 PDF、图片 的场景&#xff0c;我们来看看 Java 开发中怎么解决这个问题的。 Word 转 PDF Word 转 PDF 分为商用 Aspose 方案和开源 Apache POIiText 方案。 Aspose 方案 这种方式在目前来看应该是最好的&#xff0c;无论是转换的速度还是成功的概…

《Linux运维总结:Centos7.6之OpenSSH7.4升级版本至9.4》

一、环境信息 操作系统&#xff1a;Centos7.6.1810 OpenSSH_7.4p1, OpenSSL 1.0.2k-fips 如下图所示&#xff1a; 注意&#xff1a;升级后由于加密算法的区别&#xff0c;低版本的SSH工具可能无法连接&#xff0c;建议改用Xshell7或SecureCRT9.0以上版本。 二、注意事项 1、检…

URL 管理器

基本介绍 对外接口 对外提供两个接口&#xff1a;一个可以提取URL&#xff0c;一个可以增加URL&#xff0c;分别对应图上的1和2。 当要爬取某个网页时&#xff0c;则可以从1接口提取出该网页的URL进行爬取。 有时候爬取的网页内容中会包含别的网页链接&#xff0c;即包含有U…

【LeetCode-简单题】剑指 Offer 05. 替换空格

文章目录 题目方法一&#xff1a;常规做法&#xff1a;方法二&#xff1a;双指针做法 题目 方法一&#xff1a;常规做法&#xff1a; class Solution {public String replaceSpace(String s) {int len s.length() ;StringBuffer str new StringBuffer();for(int i 0 ; i &l…

unity 使用Photon进行网络同步

Pun使用教程 第一步&#xff1a;请确保使用的 Unity 版本等于或高于 2017.4&#xff08;不建议使用测试版&#xff09;创建一个新项目。 第二步&#xff1a;打开资源商店并找到 PUN 2 资源并下载/安装它。 导入所有资源后&#xff0c;让 Unity 重新编译。 第三步&#xf…

Java文字描边效果实现

效果&#xff1a; FontUtil工具类的完整代码如下&#xff1a; 其中实现描边效果的函数为&#xff1a;generateAdaptiveStrokeFontImage() package com.ncarzone.data.contentcenter.biz.img.util;import org.springframework.core.io.ClassPathResource; import org.springfr…

Prompt-To-Prompt——仅通过文本进行图像编辑

文章目录 1.摘要2.算法2.1 Cross-attention in text-conditioned Diffusion Models2.2 Controlling the Cross-attentionWord SwapAdding a New PhraseAttention Re–weighting 3.应用Text-Only Localized EditingGlobal editingFader Control using Attention Re-weightingRea…

HPC集群自动弹性扩缩的两种实现方式

常青藤 HPC常青园 2023-07-28 19:48 发表于北京 弹性扩缩技术正在成为HPC集群中的一项重要技术。它可以根据实际需求动态调整集群资源&#xff0c;应对用户负载的波动。对于运维团队来说&#xff0c;自动弹性扩缩能够减轻集群运维负担&#xff0c;提高集群资源利用率&#xff0…

Kettle REST Client获取token调用接口解析JSON入文件实例

Kettle REST Client获取token调用接口解析JSON入文件实例 需求说明 通过kettle组件调用接口并解析JSON成结构化数据入文件。 完整实例 解决方法 利用生成记录组件定义URL参数通过REST ClENT组件请求得到TOKEN通过JSON INPUT组件解析接口请求的结果通过JAVASCRIPT组件拼接生…

储存高精长整型の另一种思路——二维数组

利用二维数组储存高精长整型 题目解题思路问题解决代码实现总结反思 题目 luogu P2437 蜜蜂路线 解题思路 最初只觉得是一道很简单的递推 先考虑从第一个点出发的情况&#xff0c;对于第 k ( k ≥ 3 ) k (k≥3) k(k≥3)个点&#xff0c;路线数表示如下&#xff1a; a [ k …

【二叉搜索树】将有序数组转换为二叉搜索树-力扣 108 题

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…