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

news2024/11/19 2:47:52

一次内存泄露分析

背景情况

编写了一个大数据基础组件的可用性监控程序,采用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.coloradmin.cn/o/1707831.html

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

相关文章

短视频拍摄方式有哪些:四川鑫悦里文化传媒有限公司

​短视频拍摄方式有哪些 在数字化时代&#xff0c;短视频以其短小精悍、传播迅速的特点&#xff0c;成为了人们表达自我、分享生活的重要工具。然而&#xff0c;想要制作出引人入胜的短视频&#xff0c;除了创意和构思&#xff0c;拍摄方式的选择也至关重要。四川鑫悦里文化传…

详谈 Java中的list.forEach()和list.stream().forEach() 异同点

涉及的文章链接&#xff1a;ArrayList 循环Remove遇到的坑 一、想总结本篇博客的原因 在日常开发中&#xff0c;需要对集合数据进行或多或少的赋值修改&#xff0c;那么循环赋值或者做一些处理就是最常见的一种操作了&#xff0c;但是用习惯了stream流&#xff0c;所以在循环的…

聚合网卡和Wondershaper限速的一些问题(速度减半问题)

首先我们来了解一下聚合网卡&#xff1a; 聚合网卡&#xff0c;又称为链路聚合组&#xff08;LAG, Link Aggregation Group&#xff09;、端口汇聚&#xff08;Port Trunking&#xff09;、以太通道&#xff08;Ethernet Bonding&#xff09;等&#xff0c;是一种网络技术&…

【OpenCV】图形绘制与填充

介绍了绘制、填充图像的API。也介绍了RNG类用来生成随机数。相关API&#xff1a; line() rectangle() circle() ellipse() putText() 代码&#xff1a; #include "iostream" #include "opencv2/opencv.hpp"using namespace std; using namespace cv…

Git和plink

安装git的话首先进入到git官网进行下载Git - Downloading Package (git-scm.com) &#xff0c;点击便会自动进行下载。 安装plink时也是根据自己电脑的版本号选择进行安装&#xff0c;我的是windows的64位&#xff0c;由此选择以上版本进行安装&#xff0c;这一个下载完成之后不…

react中子传父信息

思路是&#xff1a; 在父组件定义一个函数接受参数&#xff0c;接收的参数用于接收子组件的信息&#xff0c;把函数传给子组件&#xff0c;子组件调用父亲传来的函数并把要告诉父亲的话传到函数中&#xff0c;就实现了子传父消息 import { useState } from reactimport { use…

JavaWeb笔记整理+图解——服务器渲染技术之EL表达式与JSTL

上一篇我们讲解了什么是服务器渲染技术和jsp&#xff0c;今天我来整理一下EL表达式和JSTL的笔记与图解&#xff0c;让我们的jsp页面更加的简洁与可维护。 没看过上一期的小伙伴可以看上一期的笔记&#xff1a; JavaWeb笔记全整理——JSP服务器渲染技术-CSDN博客 一、EL表达式…

揭开 SOCKS5 有哪些强大的功能?

在在线隐私和安全领域&#xff0c;SOCKS5 是一种多功能且功能强大的协议&#xff0c;为用户提供了一种无缝的方式来加密他们的互联网流量、绕过防火墙并以增强的匿名性和灵活性访问网络。无论您是担心在线监控、地理封锁还是数据隐私&#xff0c;了解如何利用 SOCKS5 的功能都可…

Apache-Doris单机部署

参考&#xff1a; 快速体验 Apache Doris - Apache Doris 1、Apache Doris是一款 基于MPP架构的高性能、实时的分析型数据库&#xff0c;以高效、简单、统一的特点被人们所熟知&#xff0c;仅需 亚秒级响应时间即可返回海量数据下的查询结果&#xff0c;不仅可以支持高并发的点…

ue引擎游戏开发笔记(46)——简单UI设计

1.需求分析&#xff1a; 玩家操作界面应该有基本的准星和血量显示等内容&#xff0c;告知玩家角色的基本状态。 2.操作实现&#xff1a; 1.较为简单的内容&#xff0c;在控件面板建立相应内容&#xff0c;之后链接到游戏中即可&#xff01; 2.先做准星&#xff0c;在画布上搞个…

Day 56 647. 回文子串 516.最长回文子序列

回文子串 给定一个字符串&#xff0c;你的任务是计算这个字符串中有多少个回文子串。 具有不同开始位置或结束位置的子串&#xff0c;即使是由相同的字符组成&#xff0c;也会被视作不同的子串。 示例 1&#xff1a; 输入&#xff1a;“abc”输出&#xff1a;3解释&#xf…

蓝桥杯Web开发【大学组:国赛】2022年真题

1.分一分 如果给你一个数组&#xff0c;你能很快将它分割成指定长度的若干份吗&#xff1f; 1.1 题目问题 请在 js/index.js 文件中补全函数 splitArray 中的代码&#xff0c;最终返回按指定长度分割的数组。 具体要求如下&#xff1a; 将待分割的&#xff08;一维&#x…

“星战之父”乔治・卢卡斯吐槽好莱坞“几乎没有原创思维”,AI 将“不可避免”用于电影制作

《星球大战》系列的创作者乔治・卢卡斯&#xff08;George Lucas&#xff09;在 1977 年奠定了电影制作传奇人物的地位&#xff0c;他对当今电影的状况及其发展方向有一些自己的看法。 他在 2024 年戛纳电影节上谈到了当前的电影行业。现年 80 岁的卢卡斯在接受法国媒体 Brut …

STM32建立工程问题汇总

老版本MDK&#xff0c;例如MDK4 工程内容如下&#xff1a; User文件夹中存放main.c文件&#xff0c;用户中断服务函数&#xff08;stm32f1xx.it.c&#xff09;&#xff0c;用户配置文件&#xff08;stm32f1xx_hal_conf.h&#xff09;等用户程序文件&#xff0c;或者mdk启动程序…

GIT提交:.husky/pre-commit: line 2: .husky/_/husky.sh: No such file or directory

GIT提交&#xff1a;.husky/pre-commit: line 2: .husky/_/husky.sh: No such file or directory 一些项目添加了代码提交校验和格式化&#xff0c;在windows下会忽略.husky下文件提交导致git数据丢失。 处理方案&#xff1a; 方案01&#xff1a;补齐缺失的文件 1.1 项目根…

最新斗音评论区截流拓客,自动引流【引流软件+使用教程】

面对社交媒体的蓬勃生长&#xff0c;加粉和拓展客户群成为品牌及个体的当务之急。新推出的一款技术工具恰到好处地迎合了这一需求&#xff0c;提供了一个多功能、适用性强的增粉与互动解决方案。该工具与抖音平台的所有版本兼容&#xff0c;消除了对特定版本的依赖。 利用这一…

微服务架构-聚合设计模式

微服务架构-聚合设计模式 聚合器&#xff08;Aggregator&#xff09;设计模式&#xff1a;用于将来自多个微服务的数据&#xff0c;聚合成一个统一的响应&#xff0c;提供给客户端。 聚合模式的核心思想&#xff1a;是使用一个聚合器服务&#xff08;Aggregator Service&#…

greendao实现增删改查

说明&#xff1a;最近碰到一个需求&#xff0c;在安卓上使用greendao框架&#xff0c;实现增删改查数据 效果图&#xff1a; step1: // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript {repositories {go…

26计算机操作系统408考研--操作系统处理机调度篇章(五)

文章目录 一、调度简介死锁一、调度简介 计算机系统中,处理器和内存资源会出现供不应求的情况,特别是多个I/O设备与主机交互,作业不断进入系统,或者是多个批处理作业在磁盘的后备队列中等待进入内存的情况。操作系统在管理有限的资源的同时,需要考虑如何选取进入内存的作…

破解App渠道归因难题,Xinstall助你实现精准数据追踪!

在移动互联网时代&#xff0c;App的推广和运营面临着诸多挑战。其中&#xff0c;渠道归因问题一直困扰着众多推广者。如何准确追踪用户来源&#xff0c;分析不同渠道的推广效果&#xff0c;成为了摆在推广者面前的一大难题。然而&#xff0c;有了Xinstall的出现&#xff0c;这一…