【JVM】一次JVM内存泄露分析处理

一次内存泄露分析

背景情况

编写了一个大数据基础组件的可用性监控程序,采用Bootstrap监测端口的方式,使得方法常驻(main线程常驻),通过一个调度线程ScheduledThreadPoolExecutor,定时的调动监测任务。

监测任务中,通过一个工作的线程池,执行Callable<?> 的任务,每个组件的检测一个Callable Task。要求每个检测带回检测结果,所以使用了Callable。

定时调度的监测任务

public void run() {try {boolean alertFlag = false;long t1 = System.currentTimeMillis();StringBuilder htmlBody = new StringBuilder();for (Monitor monitor : monitorList) {// 返回数据结果Map中有两个值, 一个key为componentName,值为组件的名称;一个key为组件的名称.值为此组件连续发生异常的数量final Future<?> future =ThreadPool.submitTask(() -> {Map<String, String> map = new HashMap<>();StringBuilder result = new StringBuilder();result.append(HTML_BR).append("【").append(monitor.getName()).append("】").append(HTML_BR);try {result.append(monitor.monitor());} catch (Exception e) {result.append(MonitorSupport.ERROR_INFO).append(HTML_BR);componentExceptionNum.put(monitor.getName(), componentExceptionNum.get(monitor.getName()) + 1);LOGGER.error("{}执行异常", monitor.getName(), e);} finally {map.put(monitor.getName(), result.toString());map.put(MONITOR_RESULT_KEY, monitor.getName());}return map;});list.add(future);}// 执行的最大时长应该是监控任务调度的周期也就是monitorInterval,// 后期优化思路:在快要超时的前1分钟,将未完成的任务取消cancel(true),快速返回。do {Iterator<Future<?>> it = list.iterator();while (it.hasNext()) {Future<?> e = it.next();try {@SuppressWarnings("unchecked")Map<String, String> map = (HashMap<String, String>) e.get(5, TimeUnit.MINUTES);if (e.isDone()) {try {if (map.get(map.get(MONITOR_RESULT_KEY)).contains(MonitorSupport.ERROR_INFO)) {htmlBody.insert(0, map.get(map.get(MONITOR_RESULT_KEY)));Integer newComponentExceptionNum = componentExceptionNum.get(map.get(MONITOR_RESULT_KEY)) + 1;if (newComponentExceptionNum >= alertThreshold.get(map.get(MONITOR_RESULT_KEY))) {alertFlag = true;} else {componentExceptionNum.put(map.get(MONITOR_RESULT_KEY), newComponentExceptionNum);}if (ComponentEnum.HBASE.getName().equals(map.get(MONITOR_RESULT_KEY))&& map.get(map.get(MONITOR_RESULT_KEY)).contains(MonitorSupport.HBASE_RESTORE_SETTINGS_ERROR)) {alertFlag = true;}} else {componentExceptionNum.put(map.get(MONITOR_RESULT_KEY), 0);htmlBody.append(map.get(map.get(MONITOR_RESULT_KEY)));}String a = map.get(map.get(MONITOR_RESULT_KEY));LOGGER.info("执行结果={}.", a);} catch (Exception ex) {LOGGER.error("执行结果获取异常", ex);}it.remove();} else {LOGGER.warn("{}执行超时", map.get(MONITOR_RESULT_KEY));e.cancel(true);makeTimeOutInfo(htmlBody);alertFlag = true;it.remove();}e.cancel(true);} catch (Exception e1) {LOGGER.error("执行异常", e1);}}} while (!list.isEmpty());long t3 = System.currentTimeMillis();LOGGER.info("Monitoring spent total time: [{}]ms.", (t3 - t1));htmlBody.append("<div><br/></div>");if (LOGGER.isDebugEnabled()) {LOGGER.debug("检查结果:{}{}{}", HTML_HEAD, htmlBody, HTML_FOOD);}// 异常邮件告警if (alertFlag) {SNEmail.sendHtmlEmail(HTML_HEAD + htmlBody + HTML_FOOD);LOGGER.warn("resetComponentExceptionNum");resetComponentExceptionNum(mcs, componentExceptionNum);}} catch (Exception e) {LOGGER.warn("未处理异常异常。", e);}}

问题现象

第一次现象:执行的时候,主线程没有报错,监测任务无法调度起来了。对监测任务增加了外部的try catch,看看是不是异常被吞掉了。

第二次现象:等待了近一个月(缓慢泄露,最为致命),出现了,大量OOM(Java heap space)的报错。堆内存溢出了。

内存问题分析

使用MAT( Eclipse Memory Analysis Tools )工具进行分析。下载安装参考:使用MAT进行内存问题定位

查询java默认采用的垃圾回收器的命令:

java -XX:+PrintCommandLineFlags -version

查询某个java进程采用的垃圾回收器的命令:

12910是pid

jcmd 12910 VM.flags

结果

12910:
-XX:CICompilerCount=12 -XX:ConcGCThreads=3 -XX:G1HeapRegionSize=2097152 -XX:GCLogFileSize=33554432 -XX:InitialHeapSize=4294967296 -XX:MarkStackSize=4194304 -XX:MaxHeapSize=4294967296 -XX:MaxNewSize=2575302656 -XX:MinHeapDeltaBytes=2097152 -XX:NumberOfGCLogFiles=5 -XX:+PrintAdaptiveSizePolicy -XX:+PrintGC -XX:+PrintGCApplicationConcurrentTime -XX:+PrintGCApplicationStoppedTime -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintTenuringDistribution -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC -XX:+UseGCLogFileRotation

-XX:+UseG1GC参数来启用G1GC。

jmap生存内存快照

./jmap -dump:format=b,file=/opt/logs/java_pid27258.hprof 27258

利用MAT生成报告

相关命令:./ParseHeapDump.sh java_pid27258.hprof org.eclipse.mat.api:suspects org.eclipse.mat.api:overview org.eclipse.mat.api:top_components,执行命令的用户需要配置jdk(用的JDK8)

报告分析

​ 产生三个文件java_pid27258_Leak_Suspects.zip,java_pid27258_System_Overview.zip ,java_pid27258_Top_Components.zip

查看占用内存对象中高占比的对象Top Consumers

在这里插入图片描述

查询溢出原因猜测Leak Suspects

在这里插入图片描述

猜想

看这个对象,应该是ES客户端的Sniff线程。使用RestHighLevelClient使用为了实现节点嗅探增加的Sniff。

处理

尝试了highLevelClient.close()highLevelClient.getLowLevelClient().close(),发现这个Sniff线程竟然关不掉。关不掉,又在不停创建新的客户端,那肯定会出现内存溢出了。由于监控ES是不停的创建新ES客户端,并非以保证长连接的要求去实现的。所以去掉了了Sniff功能。

以下是开启Sniff的写法。

    private RestHighLevelClient initRestHighLevelClientByIp(String ip, Integer port) {HttpHost[] httpHosts = buildHttpHosts(ip, port);// 开启嗅探SniffOnFailureListener sniffOnFailureListener = new SniffOnFailureListener();RestHighLevelClient highLevelClient = new RestHighLevelClient(RestClient.builder(httpHosts).setFailureListener(sniffOnFailureListener));// 嗅探器可以通过setSniffIntervalMills(以毫秒为单位)更新一次节点列表,Sniffer sniffer = Sniffer.builder(highLevelClient.getLowLevelClient()).setSniffAfterFailureDelayMillis(5 * 60000).build();sniffOnFailureListener.setSniffer(sniffer);return highLevelClient;}

重新启动程序。观察内存变化(多次jmap,看生成的hprof文件的大小变化)。

再次使用MAT生成内存报告,查询泄露情况

在这里插入图片描述

发现有java.lang.ref.Finalizer的占比挺高的、但是JVM垃圾回收处于正常运行了。

TODO 什么是java.lang.ref.Finalizer,下次分析学习。

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

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

相关文章

adb获取包名和界面名

adb获取包名和界面名 mac adb shell dumpsys window windows | grep mFocusedApp windows adb shell dumpsys window windows | findstr mFocusedApp 这个是在当前手机打开哪个界面获取的就是哪个界面的包名与界面 注意第一次连接时会有提示&#xff0c;需要连接两次才可以 …

【AI算法岗面试八股面经【超全整理】——机器学习】

AI算法岗面试八股面经【超全整理】 概率论信息论机器学习深度学习CVNLP 目录 1、回归损失函数2、分类损失函数3、误差&#xff08;Error&#xff09;、偏差&#xff08;Bias&#xff09;、方差&#xff08;Variance&#xff09;4、PCA&#xff08;Principle Component Analysi…

哈希算法教程(个人总结版)

背景 哈希算法&#xff08;Hash Algorithm&#xff09;是一种将任意长度的输入&#xff08;也称为消息&#xff09;转换为固定长度的输出&#xff08;也称为哈希值、散列值、摘要&#xff09;的算法。哈希算法在计算机科学中有着广泛的应用&#xff0c;包括数据存储、数据检索…

【软考网络工程师-2023年5月上午真题】

1、固态硬盘的存储介质是&#xff1a; 闪存 。 &#xff08;固态硬盘SSD的存储介质是Flash&#xff0c;也叫闪存&#xff0c;U盘中的存储介质也是闪存&#xff1b;机械硬盘HDD的存储介质是磁盘&#xff0c;依靠磁盘转动实现读写&#xff0c;机械硬盘内涵机械臂和磁道&#xff0…

四川汇聚荣聚荣科技有限公司好不好?

在当今科技飞速发展的时代&#xff0c;企业要想在激烈的市场竞争中脱颖而出&#xff0c;必须具备强大的技术实力和良好的市场口碑。那么&#xff0c;作为一家专注于科技创新的公司&#xff0c;四川汇聚荣聚荣科技有限公司究竟如何呢?接下来&#xff0c;我们将从四个方面进行详…

扔掉 MacBook,挑战带OrangePi出差!

背景 由于工作需要&#xff0c;博主经常会到各大企业的自建机房中私有化部署公司的软件产品。 在某些企业自建机房中&#xff0c;有时给到全新的机器&#xff0c;没有基础环境&#xff0c;甚至有的还无法互联网&#xff0c;而且因为近几年CentOS的停止更新&#xff0c;服务器…

【Linux】Linux的权限_2 + Linux环境基础开发工具_1

文章目录 三、权限3. Linux权限管理修改文件的拥有者和所属组 4. 文件的类型5. 权限掩码 四、Linux环境基础开发工具1. yumyum 工具的使用 未完待续 三、权限 3. Linux权限管理 修改文件的拥有者和所属组 在上一节我们讲到如何更改文件的访问权限&#xff0c;那我们需要更改…

删除最后一个字符 js vue 前端

substring()、slice()、substr() let str ;let str1 "abcdef"; str str1.substring(0, str1.length - 1);let str2 "abcdef"; str str2.slice(0, str2.length - 1);let str3 "abcdef"; str str3.substr(0, str3.length - 1); 官方想废弃…

光伏智慧化运营解决方案的应用和价值

在社会对新能源需求的不断扩大&#xff0c;光伏已经成为了可再生能源的重要组成部分&#xff0c;随着光伏电站数量和规模的不断扩大&#xff0c;相关企业和用户都就开始关注如何能够高效精准的进行电站管理&#xff0c;对此&#xff0c;鹧鸪云提出了光伏智慧化运营解决方案&…

【官方指南】3ds Max中纹理贴图问题及正确解决方案

在使用3ds Max进行设计和制作时&#xff0c;纹理贴图是一个非常重要的环节。然而&#xff0c;许多用户在使用过程中常会遇到各种纹理贴图问题。为此&#xff0c;Autodesk官方提供了一些有效的解决方案&#xff0c;可以解决90%的纹理贴图难题。这里小编都帮大家整理好了&#xf…

简化跨网文件传输摆渡过程,降低IT人员工作量

在当今数字化时代&#xff0c;IT企业面临着日益增长的数据交换需求。随着网络安全威胁的不断演变&#xff0c;网关隔离成为了保护企业内部网络不受外部威胁的重要手段。然而&#xff0c;隔离的同时&#xff0c;企业也需要在不同网络间安全、高效地传输文件&#xff0c;这就催生…

线性回归计算举例

使用正规方程计算&#xff08;一元线性回归&#xff09; import numpy as np import matplotlib.pyplot as plt # 转化成矩阵 X np.linspace(0, 10, num 30).reshape(-1, 1) # 斜率和截距&#xff0c;随机生成 w np.random.randint(1, 5, size 1) b np.random.randint(1,…

vue:动态表格操作栏操作显示隐藏控制

文章目录 动态数据格式子组件接收父组件传递过来的数据 整动态数据格式程 1.调用表格组件的父组件 <template> <Table :PropTableS"PropTableS" ></Table> </template>let PropTableS ref({keyS:{selection:{type:selection,width:50,},…

C++指针、数组越界异常、栈区、堆区,和静态区

文章目录 一、指针引用运算符(&)和引用运算符(*)二、数组越界异常三、C++的栈区、堆区,和静态区一、指针 C ++中指针能够直接操作计算机内存中的数据。 可以根据需要分配和取消分配内存中的任何空间。 指针变量是指向内存中另一个变量所指向的特定地址的变量。 声明一个…

使用 pyhttptest 进行 RESTful API 测试的最佳实践

现在&#xff0c;无论是开发还是使用服务&#xff0c;我们每个人都面临着 REST API 的挑战。同时&#xff0c;我们正处于微服务的流行时代&#xff0c;我们将业务逻辑拆分为多个独立的小服务。这些服务大多遵循 RESTful 原则&#xff0c;并使用 JSON 格式进行通信&#xff0c;因…

Qt项目使用pato mqtt C

一,下载pato mqtt C 源码 git 地址:https://github.com/eclipse/paho.mqtt.c.git git 地址可能下载不下来,提供我的gitee地址 gitee地址:https://gitee.com/chaojidahuaidan2021/paho.mqtt.c.git 二,编译共享库 clone下来后,将项目导入到Qt工程中,此时这是一个cmke工程…

三十一、openlayers官网示例Draw Features解析——在地图上自定义绘制点、线、多边形、圆形并获取图形数据

官网demo地址&#xff1a; Draw Features 先初始化地图&#xff0c;准备一个空的矢量图层&#xff0c;用于显示绘制的图形。 initLayers() {const raster new TileLayer({source: new XYZ({url: "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/…

Kubernetes和Docker对不同OS和CPU架构的适配关系

Docker Docker官网对操作系统和CPU架构的适配关系图 对于其他发行版本&#xff0c;Docker官方表示没有测试或验证在相应衍生发行版本上的安装&#xff0c;并建议针对例如Debian、Ubuntu等衍生发行版本上使用官方的对应版本。 Kubernetes X86-64 ARM64 Debian系 √ √ Re…

贪心算法[1]

首先用最最最经典的部分背包问题来引入贪心的思想。 由题意可知我们需要挑选出价值最大的物品放入背包&#xff0c;价值即单位价值。 我们需要计算出每一堆金币中单位价值。金币的属性涉及两个特征&#xff0c;重量和价值。 所以我们使用结构体。 上代码。 #include <i…

基于单张图片快速生成Metahuman数字人(模型贴图绑定)的工作流演示

基于单张图片快速生成Metahuman数字人&#xff08;模型贴图绑定&#xff09;的工作流演示 MetahumanModeler, 是我基于facebuilder以及metahuman的理解开发而成&#xff0c;插件可以基于单张图片生成metahuman拓扑结构的面部3d模型&#xff0c;同时生成对应的面部的贴图&#…