你真的遇到过线上的OOM问题吗?真的遇到了你知道怎么处理吗?说实话这种问题还是需要一点一点去排查的,可能并没有你想的那么简单,不过也有可能没有你想的那么难!感兴趣的话可以3~5分钟看下,只讲有用的,不说废话!
1、前因
周三早上9点多,部门技术经理反馈需要协助看两个线上问题,这项目都是几年前的古董项目,很少有人动,更别说了解业务场景了,奈何打工人,只能说没问题...
2、问题描述
-
问题一属于业务型问题,也是在我的猜测和生产日志两头辅助下,花费了一天的时间找到了问题所在,关键源代码还是猜的在哪个项目里面,也是无语了...
-
问题二也是本文将要说的重点内容,OOM这种经常说但是不经常遇到的问题,因此本次做下记录...
3、工欲善其事,必先利其器
俗话说得好”工欲善其事,必先利其器“,要做OOM的分析,必经不是顶级大神看下日志就知道问题在哪,因此还是需要借助相应的工具的,由于是做OOM问题日志分析,因此最终决定在VisualVM 和MAT(Memory Analyzer Tool)两者之前选者一个,最终做了下对比,还是决定用MAT;
-
VisualVM
功能特点: 实时监控:VisualVM可以实时显示Java进程的CPU、内存、线程和GC(垃圾回收)等方面的信息,帮助开发者快速分析应用程序的性能和资源占用情况。 线程分析:能生成应用程序的线程快照(Thread Dump),帮助识别死锁、饥饿和其他线程相关的问题。 堆内存分析:可生成应用程序的堆内存快照(Heap Dump),用于分析内存泄漏、内存溢出等内存问题。 CPU和内存分析器:提供CPU和内存的采样分析器和探查器,帮助找到性能瓶颈和内存泄漏。 GC分析:显示垃圾回收的详细信息,有助于分析和优化垃圾回收策略。 插件支持:支持通过插件扩展功能,增加工具的灵活性和可用性。 优势: 综合了多个JDK命令行工具的功能,提供图形化用户界面,操作直观方便。 实时监控和分析功能强大,适合对Java应用程序进行全面的性能监控和资源分析。
-
MAT(Memory Analyzer Tool)
功能特点: 堆转储分析:专注于分析Java堆转储文件(Heap Dump),帮助开发者查找内存泄漏和减少内存消耗。 对象分析:快速计算内存中对象的占用大小,查看阻止垃圾收集器回收工作的对象,并通过报表直观展示。 报告生成:提供多种报告模式,如内存泄漏可疑点报告(Leak Suspects Report)和元件报告(Component Report),帮助开发者定位问题。 数据视图:支持按类数量、浅堆(Shallow Heap)、深堆(Retained Heap)等多种视图展示数据,方便开发者深入分析。 优势: 专注于堆内存分析,功能丰富且深入,适合解决复杂的内存问题。 提供直观的报表和视图,帮助开发者快速定位内存泄漏和消耗问题。
总结如下:
-
如果是实时监控Java应用程序的性能和资源占用情况,包括CPU、内存、线程和GC等方面,那么VisualVM可能更适合你。它提供了一个全面的监控和分析平台,让你能够实时了解应用程序的运行状态。
-
如果主要关注点是内存问题,特别是堆内存中的内存泄漏和消耗问题,那么MAT更适合。它专注于堆转储文件的分析,提供了丰富的功能和视图来深入分析了解内存使用情况。
4、资源准备
-
OOM日志文件
hs_err_***.log
和hs_heapdp_**.hprof
文件 -
MAT本地安装,具体安装步骤如下图:
5、hs_err_pid.log文件分析
5.1、文件结构说明
-
日志头信息【重要】
日期和时间:记录日志生成的具体时间。 JVM版本信息:包括JVM实现的名称、版本、构建信息等。 操作系统信息:如操作系统名称、版本、架构等。 错误信号:如 SIGSEGV, SIGBUS 等,表示导致JVM崩溃的操作系统信号。
-
错误摘要,导致crash的线程信息【重要】
一条总结性的错误信息,如 "A fatal error has been detected by the Java Runtime Environment:",紧接着是具体的错误描述。显示引发崩溃的线程的详细信息,包括线程ID、线程名称以及堆栈跟踪。
-
线程转储(Thread Dump),所有线程信息
当前所有活动线程的状态,包括线程ID、线程名称、堆栈跟踪(stack trace)。这对于分析哪个线程引发了错误非常关键。一般存在内存泄漏的话就在这些线程里面。
-
安全点和锁信息
包括CPU寄存器的内容,这对于理解崩溃时的硬件状态很重要,特别是对于硬件相关的错误。描述了在崩溃时刻哪些线程持有锁,以及锁的状态,这对于分析死锁或竞争条件等问题非常有用。
-
堆信息
包括堆的大小、使用情况以及对象分配的统计信息,可以帮助诊断内存泄漏或内存不足的问题。
-
本地代码缓存
展示了JIT编译后的本地代码缓存,这对于理解性能问题和潜在的代码优化点有帮助。
-
编译事件
记录了JIT编译器的活动,包括编译的热点方法、编译级别等,有助于分析性能瓶颈。
-
gc相关记录【重要】
包含最近的垃圾收集活动,包括收集的类型、前后堆的使用情况等,这对于分析GC相关的性能问题非常重要。
-
JVM内存映射和JVM启动参数
显示了JVM在物理内存中的布局,包括各个内存区域的位置和大小,这对于诊断内存访问错误非常有用。列出了启动JVM时使用的命令行参数,这对于复现问题环境非常关键。
-
服务器信息
包括操作系统版本、处理器型号、CPU核心数等,这些信息有助于理解运行环境。
5.2、梳理出来的信息关键点
PS:通过hs_err_pid.log可以得出一个大体的结论:异常报错为oom,导致oom的线程为‘RxComputationScheduler-3’但是不一定是问题线程,只能说明刚好这个线程创建对象时内存溢出了,另外年轻代频繁GC说明大量创建新对象。
6、MAT分析hs_heapdp_pid.hprof文件
前提:File->Open Help Dump导入hprof文件,功能说明
-
Dominator tree
功能 展现对象的支配关系图,并给出对象支配内存的大小(支配内存等同于 Retained Heap,即其被 GC 回收可释放的内存大小) 支持排序、支持按 package、class loader、super class、class 聚类统计。
使用场景 开始 Dump 分析时,首先应使用 Dominator tree 了解各支配树起点对象所支配内存的大小,进而了解哪几个起点对象是 GC 无法释放大内存的原因。 当个别对象支配树的 Retained Heap 很大存在明显倾斜时,可以重点分析占比高的对象支配关系,展开子树进一步定位到问题根因,如下图中可看出最终是 TaskThread线程对象持有的 ArrayList 过大。
Class Name : 类名称,java类名** Objects : 类的对象的数量,这个对象被创建了多少个** Shallow Heap :一个对象内存的消耗大小,不包含对其他对象的引用** Retained Heap :是shallow Heap的总和,也就是该对象被GC之后所能回收到内存的总和
基本上由上图我们已经初步判断出来问题所在了,下面就找一下,是哪个查询导致的问题
-
thread_view
主要用于查询线程的堆栈信息
找到对应的业务方法之后,需要配合oom异常时对应的业务日志进行判断,找oom时刻同样线程(11)的哪个业务日志基本上就能很快的定位到问题了,最终定位问题为一个查询条件未传入导致获取大量数据,我也是醉了...
问题已找到,不是什么大问题,但是查起来的确比较不容易,完结撒花,稍等,在告诉你一个小技巧...
7、使用技巧
-
Leak Suspects
功能:具备自动检测内存泄漏功能,罗列可能存在内存泄漏的问题点。
使用入口:一般当存在明显的内存泄漏时,分析完Dump文件后就会展现,也可以如下图在 MAT 主页 → Leak Suspects。
使用场景:需要查看引用链条上占用内存较多的可疑对象。这个功能可解决一些基础问题,但复杂的问题往往帮助有限。
是不是更容易,不过针对明显问题可以,看当前这个问题的饼图你就知道
8、参考文献
一文深度讲解JVM 内存分析工具 MAT及实践(建议收藏)-阿里云开发者社区 (aliyun.com)
MAT(Memory Analyzer Tool)工具使用超详细版-CSDN博客
Java程序内存分析:使用mat工具分析内存占用 - 孤剑 - 博客园 (cnblogs.com)
添加公众号了解更多,定期分享、绝对实用,绝对对你有帮助!