返回顶部
首页 > 资讯 > 后端开发 > JAVA >Java 使用 VisualVM 排查内存泄露
  • 121
分享到

Java 使用 VisualVM 排查内存泄露

javajvmlinux 2023-08-20 17:08:44 121人浏览 泡泡鱼
摘要

文章目录 1. 问题发生2. 排查过程2.1 初步排查2.2 Visual VM 内存分析2.3 代码分析 1. 问题发生 线上突发告警,笔者负责的一个服务老年代内存使用率到达 75% 阈值,于是立即登录监控系统查看数

1. 问题发生

线上突发告警,笔者负责的一个服务老年代内存使用率到达 75% 阈值,于是立即登录监控系统查看数据。拉长时间周期,查看最近 7 天的 GC 和老年代内存占用,监控截图如下。可以看到老年代占用内存的最低点在逐步抬升,初步判断是发生了内存泄露

在这里插入图片描述

2. 排查过程

2.1 初步排查

从监控上看,这个服务的两个实例老年代内存占用情况并不一致,其中疑似发生内存泄露的是跑脚本的机器。于是登录到目标机器,首先执行 jmap -histo 1 | head -n 100 命令查看目标进程的堆内存占用前 100 的对象,发现其中 SkyWalking 的一个 trace 追踪对象 NoopSpan 实例总数达到了 2600 万之巨,内存占用也达到 600M,明显不正常
在这里插入图片描述

2.2 Visual VM 内存分析

由于生产环境控制严格,不允许在线 dump 堆内存数据,于是在预发环境执行 jmap -dump:fORMat=b,file=/tmp/dump 1 命令,将有相同问题的 java 进程的堆内存 dump 下来。下载拿到 dump 文件后,需要打开 VisualVM 加载该文件,以下为操作步骤

  1. 首先打开 VisualVM,点击截图中的按钮加载 dump 文件
    在这里插入图片描述

  2. dump 文件加载后,点击截图中框出来的按钮,切换选项卡为查看对象
    在这里插入图片描述

  3. 由于笔者初步排查已经确定了可疑的实例为 NoopSpan,故在对象选项卡界面直接过滤该对象,并展示其 相关引用、GC root需注意 GC root 是引用链的起点,从 VisualVM 的分析可以看到 NoopSpan 的实例都是以 LinkedList 节点的形式存在,引用链条为 FastThreadLocalThread -> threadLocals(ThreadLocalMap) -> table(ThreadLocalMap$Entry[] 数组) -> [1](ThreadLocalMap$Entry 数组第一个元素) -> value(键值对 ThreadLocalMap$Entry 的值) -> activeSpanStack (SkyWalking 的 TracingContext 内部暂存 span 的 LinkedList) -> 链表的一级级前后指针,至此可以猜测是 ThradLocal 使用不当(例如 ThreadLocal 使用后没有remove)导致内存泄露
    在这里插入图片描述

  4. 确定了引用链,则可以看到 NoopSpan 应该是被封装为 LinkedList 的节点被保存在对象TracingContext#1 的内部链表 activeSpanStack 中。此时查看该对象的链表的具体元素数据,可以看到总共有1万多个元素,点开第一个节点,查看该 LocalSpan 的名称,确定当前 SkyWalking 的 trace 记录的起点为这个 LocalSpan 的创建
    在这里插入图片描述

2.3 代码分析

  1. 项目中搜索上一节分析出的 LocalSpan 名称,发现创建该 Span 主要是为了在多线程环境下跨线程传递 trace,创建入口为 ContextManager#createLocalSpan() 方法。这个方法会创建 Trace 上下文对象 TracingContext 并将其设置到 ThreadLocal 中,创建出 TracingContext 对象后还会调用其相关方法创建 LocalSpan 对象,并将创建的 LocalSpan 对象存入 TracingContext 内部的 activeSpanStack 链表。至此基本印证了 VisualVM 的引用分析,大致确定是 ThreadLocal 的使用导致了内存泄露

     public static AbstractSpan createLocalSpan(String operationName) {     operationName = StringUtil.cut(operationName, OPERATION_NAME_THRESHOLD);     AbstractTracerContext context = getOrCreate(operationName, false);     return context.createLocalSpan(operationName); } private static AbstractTracerContext getOrCreate(String operationName, boolean forceSampling) {     AbstractTracerContext context = CONTEXT.get();     if (context == null) {         if (StringUtil.isEmpty(operationName)) {             if (logger.isDebugEnable()) {                 logger.debug("No operation name, ignore this trace.");             }             context = new IgnoredTracerContext();         } else {             if (EXTEND_SERVICE == null) {                 EXTEND_SERVICE = ServiceManager.INSTANCE.findService(ContextManagerExtendService.class);             }             context = EXTEND_SERVICE.createTraceContext(operationName, forceSampling);         }         CONTEXT.set(context);     }     return context; }
  2. 我们知道,在线程池环境下使用 ThreadLocal 如果忘记 remove 很容易发生内存泄漏。继续阅读源码,发现 ThreadLocal 被移除的触发点在 ContextManager#stopSpan() 方法,该方法每调用一次就会将之前添加到 TracingContext 内部的 activeSpanStack 链表中的 Span 移除,直到链表元素数量为 0 才会去 remove 掉 ThreadLocal

     public static void stopSpan() {     final AbstractTracerContext context = get();     if (Objects.isNull(context)) {         return;     }     stopSpan(context.activeSpan(), context); } private static void stopSpan(AbstractSpan span, final AbstractTracerContext context) {     try {         if (context.stopSpan(span)) {             CONTEXT.remove();             RUNTIME_CONTEXT.remove();         }     } catch (Throwable t) {         //     } }
  3. 此时回到项目代码一看,问题一目了然,代码中创建了 LocalSpan 但是没有调用相关方法把它 stop 掉,导致 LocalSpan 一直在 TracingContext 内部的 activeSpanStack 链表中堆积,并且由于链表前后指针的存在无法回收,最终导致了内存泄漏

来源地址:https://blog.csdn.net/weixin_45505313/article/details/131107760

--结束END--

本文标题: Java 使用 VisualVM 排查内存泄露

本文链接: https://lsjlt.com/news/376557.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Java 使用 VisualVM 排查内存泄露
    文章目录 1. 问题发生2. 排查过程2.1 初步排查2.2 Visual VM 内存分析2.3 代码分析 1. 问题发生 线上突发告警,笔者负责的一个服务老年代内存使用率到达 75% 阈值,于是立即登录监控系统查看数...
    99+
    2023-08-20
    java jvm linux
  • php内存泄露如何排查
    要排查PHP内存泄露问题,可以采取以下几个步骤:1. 使用垃圾回收机制:PHP的垃圾回收机制会自动释放不再使用的内存,可以通过在代码...
    99+
    2023-09-26
    php
  • 怎么排查Spring Boot内存泄露
    本篇内容主要讲解“怎么排查Spring Boot内存泄露”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么排查Spring Boot内存泄露”吧!为了更好地实现对项目的管理,我们将组内一个项目迁...
    99+
    2023-06-16
  • Java内存泄露怎么检查
    这篇文章主要介绍“Java内存泄露怎么检查”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Java内存泄露怎么检查”文章能帮助大家解决问题。Java是如何管理内存的:Java的内存管理就是对象的分配和...
    99+
    2023-06-03
  • 关于Spring Boot内存泄露排查的记录
    目录背景排查过程1.使用Java层面的工具定位内存区域2. 使用系统层面的工具定位堆外内存3. 为什么堆外内存没有释放掉呢?总结在项目迁移到Spring Boot之后,发生内存使用量...
    99+
    2024-04-02
  • android内存泄露:4、Toast的错误使用导致内存泄露
    目录 一、前言 二、Toast的错误使用导致内存泄露 1、新建一个 Module,写主界面 MainActivity,布局 activity_main 2、写业务逻辑 3、效果...
    99+
    2022-06-06
    toast Android
  • c# 内存泄露怎么查
    内存泄露是指应用程序无法释放不再使用的内存。检测 c# 内存泄露的方法包括:1. 使用 visual studio 内存分析器或 jetbrains dotmemory profiler...
    99+
    2024-05-11
    c#
  • Python实现内存泄露排查的示例详解
    一般情况下只有需要长期运行的项目才会去关注内存的增长情况,即使是很小部分的内存泄露经过长期的运行仍然会产生很大的隐患。 python本身也是支持垃圾的自动回收的,但是在特定的情况下也...
    99+
    2023-01-28
    Python内存泄露排查 Python内存泄露 Python内存
  • java内存泄漏怎么排查
    Java内存泄漏是指在程序运行过程中,不再使用的对象仍然占用着内存空间,导致内存无法被回收。以下是一些常见的排查内存泄漏的方法:1....
    99+
    2023-08-31
    java
  • Java 内存泄露问题详解
    目录 1、什么是内存泄露? 2、Java 中可能导致内存泄露的场景 3、长生命周期对象持有短生命周期对象引用造成的内存泄露问题示例 4、静态集合类持有对象引用造成内存泄露问题的示例 1、什么是内存泄露?         内存泄露指的是程...
    99+
    2023-09-08
    Java 内存泄露
  • Java内存泄漏的排查分析
    本篇内容介绍了“Java内存泄漏的排查分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、由来前些日子小组内安排值班,轮流看顾我们的服务,...
    99+
    2023-06-02
  • C++中如何检查内存泄露
    这篇文章给大家介绍C++中如何检查内存泄露,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。一、前言在Linux平台上 有valgrind可以非常方便的帮助我们定位内存泄漏,因为Linux在开发领域的使用场景大多是跑服务器...
    99+
    2023-06-17
  • Java内存泄漏实例排查分析
    这篇文章主要介绍“Java内存泄漏实例排查分析”,在日常操作中,相信很多人在Java内存泄漏实例排查分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java内存泄漏实例排查分析”的疑惑有所帮助!接下来,请跟...
    99+
    2023-06-16
  • java堆外内存泄漏如何排查
    在Java中,堆外内存泄漏通常是由于未正确释放使用了堆外内存的资源所导致的。下面是一些排查堆外内存泄漏的常见方法:1. 使用JVM工...
    99+
    2023-08-24
    java
  • java堆外内存泄漏怎么排查
    在Java中,堆外内存通常是通过直接内存(Direct Memory)分配的。直接内存是一种不受Java堆内存管理的内存分配方式,它...
    99+
    2023-10-27
    java
  • 使用Android Studio检测内存泄露(LeakCanary)
    内存泄露,是Android开发者最头疼的事。可能一处小小的内存泄露,都可能是毁千里之堤的蚁穴。 怎么才能检测内存泄露呢? AndroidStudio 中Memory控件台(显...
    99+
    2022-06-06
    Android Studio studio leakcanary Android
  • Java内存泄漏问题排查与解决
    前言 Java 最牛逼的一个特性就是垃圾回收机制,不用像 C++ 需要手动管理内存,所以作为 Java 程序员很幸福,只管 New New New 即可,反正 Java 会自动回收过...
    99+
    2024-04-02
  • Java内存泄漏排查的示例分析
    这篇文章将为大家详细讲解有关Java内存泄漏排查的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。在一个凄凉的午夜午夜刚过,我就被一条来自监控系统的警报吵醒了。Adventory,我们的 PPC (...
    99+
    2023-06-04
  • 深入聊聊Java内存泄露问题
    目录Java内存泄露问题附:内存泄露的典型情况总结Java内存泄露问题 所谓内存泄露就是指一个不再被程序便用的对象或变量一直被占据在内存中。 Java 中有垃圾回收机制,它可以保证一...
    99+
    2024-04-02
  • Java内存泄露问题实例分析
    本篇内容介绍了“Java内存泄露问题实例分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Java内存泄露问题所谓内存泄露就是指一个不再被程...
    99+
    2023-06-29
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作