线上OOM排查

简介: 本文介绍了JDK工具的使用方法及其应用场景。首先详细说明了`jps`、`jstack`、`jstat`和`jmap`等工具的基本用法及参数含义,帮助开发者实时监控Java进程的状态,诊断线程问题及内存使用情况。接着介绍了`jvisualvm.exe`和`MemoryAnalyzer.exe`两款内存诊断工具,展示了如何通过这些工具进行内存分析。最后,文章提供了在线上OOM故障排查的具体步骤,并给出了解决方案示例,以便开发者更好地理解和解决实际问题。

JDK工具

jps

查看服务器中当前用户下的Java进程

bash

代码解读

复制代码

usage: jps [-help]
       jps [-q] [-mlvV] [<hostid>]

Definitions:
    <hostid>:      <hostname>[:<port>]

常用参数解释:
-q : 显示Java进程的进程ID,不显示主类名称、JAR文件名和传递给主方法的参数
-m : 显示Java虚拟机启动时传递给main()方法的参数
-l : 显示主类的完整包名,如果进程执行的是JAR文件,也会显示JAR文件的完整路径
-v : 显示Java虚拟机启动时传递JVM的参数

jstack

查看Java虚拟机在当前时刻的线程快照。

线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。

线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。

bash

代码解读

复制代码

Usage:
    jstack [-l] <pid>
        (连接到正在运行的进程)
    jstack -F [-m] [-l] <pid>
        (连接到挂起的进程)
    jstack [-m] [-l] <executable> <core>
        (连接到核心文件)
    jstack [-m] [-l] [server_id@]<remote server IP or hostname>
        (连接到远程调试服务器)

Options:
    -F  强制线程转储。当 jstack <pid> 没有响应(进程挂起)时使用
    -m  打印java和native c/c++框架的所有栈信息(混合模式)
    -l  长列表。打印有关锁定的其他信息,如属于java.util.concurrent的ownable synchronizers列表.
    -h 查看帮助信息
    -help 同 -h

jstat

Java虚拟机统计信息工具,利用JVM内建的指令对Java应用程序的资源和性能进行实时的命令行的监控

bash

代码解读

复制代码

Usage: jstat -help|-options
       jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]

Definitions:
  <option>      jstat -options 得到的选项值
  <vmid>        虚拟机标识符
  <lines>       标题行之间的样本数
  <interval>    采样间隔。允许以下形式:<n>["ms"|"s"] 其中 <n> 是整数,后缀指定单位为 毫秒(“ms”)或秒(“s”)。默认单位为“ms“
  <count>       终止前要进行的采样数
  -J<flag>      将 <flag> 直接传递给运行时系统

jstat -options 选项
-class : 监视类装载、卸载数量、总空间及类装载所耗费的时间
-compiler : 输出JIT编译器编译过的方法、耗时等信息
-gc : 监视 Java堆状况,包括Eden区、2个survivor区、老年代、永久代等的容量、已用空间、GC时间合计等信息
-gccapacity : 监视内容与-gc基本相同,但输出主要关注Java堆各个区域使用到的最大和最小空间
-gccause : 与-gcutil功能一样,但是会额外输出导致上一次GC产生的原因
-gcmetacapacity : 显示关于metaspsce大小的统计信息
-gcnew : 监视新生代 GC的状况
-gcnewcapacity : 监视内容与-gcnew基本相同,输出主要关注使用到的最大和最小空间
-gcold : 监视老年代GC的状况
-gcoldcapacity : 监视内容与-gcold基本相同,输出主要关注使用到的最大和最小空间
-gcutil : 监视内容与-gc基本相同,但输出主要关注已使用空间占总空间的百分比
-printcompilation : 当前VM的执行信息

使用示例

jstat -gc <pid>  查看内存及GC情况

bash

代码解读

复制代码

C:\Users\Administrator>jstat -gc 6400
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
75776.0 81408.0 67606.5  0.0   536064.0 105037.9 1398272.0   602334.0  245488.0 236296.6 24616.0 23082.8    132    2.093   5      0.992    3.084

参数解释:
S0C:第一个幸存区的大小
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
OC:老年代大小
OU:老年代使用大小
MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间

jstat -gccause <pid> 查看导致GC的原因

bash

代码解读

复制代码

C:\Users\Administrator>jstat -gccause 6400
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT    LGCC                 GCC
 89.22   0.00  22.96  43.08  96.26  93.77    132    2.093     5    0.992    3.084 Allocation Failure   No GC

参数解释:
LGCC: 上一次GC的原因
GCC: 当前GC的原因

jmap

JDK提供的用来监视进程运行中JAVA物理内存占用情况的工具,用于生成堆转储快照,执行该指令时会影响线上服务的运行

bash

代码解读

复制代码

Usage:
    jmap [option] <pid>
        (用于连接到正在运行的进程)
    jmap [option] <executable <core>
        (用于连接到核心文件)
    jmap [option] [server_id@]<remote server IP or hostname>
        (用于连接到远程调试服务器)

where <option> is one of:
    <none>               查看进程的内存映像 类似于Solaris pmap命令
    -heap                打印 Java 堆摘要
    -histo[:live]        打印 java 对象堆的直方图;如果指定了“live”子选项,则仅计数活动对象
    -clstats             打印类加载器统计信息
    -finalizerinfo       打印等待完成的对象的信息
    -dump:<dump-options> 以 hprof 二进制格式转储 java 堆
                         dump-options:
                           live         仅转储活动对象;如果未指定,则转储堆中的所有对象。
                           format=b     二进制格式
                           file=<file>  将堆转储到 <file>
                         Example: jmap -dump:live,format=b,file=heap.bin <pid>
    -F                   与 -dump:<dump-options> <pid> 或 -histo 一起使用在 <pid> 没有响应时强制进行堆转储或直方图。此模式不支持“live”子选项
    -h | -help           打印此帮助消息
    -J<flag>             将 <flag> 直接传递给运行时系统

使用示例:

jmap -dump:format=b,file=heapdump.hprof <pid>生成进程的内存快照存储在heapdump.hprof文件中。

内存诊断工具

jvisualvm.exe

JDK提供的JVM监控工具,一般位于%JAVA_HOME%\bin目录下, 可以通过这个工具对Java进程进行实时监控或者对转储出来的堆栈文件进行分析。

MemoryAnalyzer.exe

Eclipse 提供的内存分析软件(下载地址), 可以生成饼图,看起来更加直观。

线上OOM排查

1、应用启动时增加JVM参数 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=<file>, <file> 可以是指定的文件或者目录,指定为目录时转储的文件是存储在该目录下,文件名由系统随机生成。

例如: java -Xms1024m -Xmx2048m  -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./dump -jar  test.jar

2、如果没有配置JVM参数在OOM时生成快照 使用jps -ml命令找到对应Java进程的 <pid>使用jmap -dump:format=b,file=heapdump.hprof <pid> 生成内存快照

3、使用内存分析工具打开生成的快照文件,对其进行分析,使用 jvisualvm 打开文件,可以看到导致OOM的线程,点进去查看其堆栈。

bash

代码解读

复制代码

"http-nio-39805-exec-4" daemon prio=5 tid=102 RUNNABLE
	at java.lang.OutOfMemoryError.<init>(OutOfMemoryError.java:48)
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:757)
	   Local Variable: java.security.ProtectionDomain#30
	at java.lang.ClassLoader.defineClass(ClassLoader.java:636)
	at sun.reflect.GeneratedMethodAccessor209.invoke(<unknown string>)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.sun.xml.bind.v2.runtime.reflect.opt.Injector.inject(Injector.java:125)
	   Local Variable: java.lang.String#790762
	   Local Variable: com.sun.xml.bind.v2.runtime.reflect.opt.Injector#1
	at com.sun.xml.bind.v2.runtime.reflect.opt.Injector.inject(Injector.java:48)
	at com.sun.xml.bind.v2.runtime.reflect.opt.AccessorInjector.prepare(AccessorInjector.java:51)
	at com.sun.xml.bind.v2.runtime.reflect.opt.OptimizedAccessorFactory.get(OptimizedAccessorFactory.java:128)
	   Local Variable: java.lang.reflect.Field#11880
	at com.sun.xml.bind.v2.runtime.reflect.Accessor$FieldReflection.optimize(Accessor.java:204)
	   Local Variable: com.sun.xml.bind.v2.runtime.reflect.Accessor$FieldReflection#5
	at com.sun.xml.bind.v2.runtime.property.SingleElementLeafProperty.<init>(SingleElementLeafProperty.java:45)
	   Local Variable: com.sun.xml.bind.v2.model.impl.RuntimeTypeRefImpl#5
	   Local Variable: com.sun.xml.bind.v2.runtime.property.SingleElementLeafProperty#5
	at sun.reflect.GeneratedConstructorAccessor140.newInstance(<unknown string>)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at com.sun.xml.bind.v2.runtime.property.PropertyFactory.create(PropertyFactory.java:88)
	at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.<init>(ClassBeanInfoImpl.java:135)
	   Local Variable: com.sun.xml.bind.v2.model.impl.RuntimeElementPropertyInfoImpl#5
	   Local Variable: com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl#3
	at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getOrCreate(JAXBContextImpl.java:448)
	at com.sun.xml.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:282)
	   Local Variable: java.util.Collections$EmptyList#1
	   Local Variable: com.sun.xml.bind.v2.model.impl.RuntimeTypeInfoSetImpl#2
	   Local Variable: com.sun.xml.bind.v2.runtime.JAXBContextImpl#2
	   Local Variable: com.sun.xml.bind.v2.model.impl.RuntimeClassInfoImpl#3
	at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:84)
	at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:66)
	at sun.reflect.GeneratedMethodAccessor3352.invoke(<unknown string>)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:247)
	at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:234)
	at javax.xml.bind.ContextFinder.find(ContextFinder.java:441)
	   Local Variable: java.io.BufferedReader#1
	at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:641)
	at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:584)

4、根据堆栈和内存分析,可能是由于JAXBContext.newInstance 方法短时间被频繁调用导致占用大量内存且未被释放(该方法在定时任务中被调用)

5、JAXBContext 是可以复用的,那么可以考虑持有一个对象池,优先从对象池中加载 JAXBContext ,池中取不到对象时再 new 一个对象,然后放入对象池中。此方法在JAXBContext 类型较少时可以适用,如果JAXBContext 类型很多的情况下,对对象池的处理逻辑需要重新编写,不能一直往里添加对象,可以考虑在对象池的大小超过一定阈值时使用先进先出或最近最少算法淘汰之前添加的对象。

java

代码解读

复制代码


private static ConcurrentHashMap<String, JAXBContext> JAXBCONTEXT_MAP = new ConcurrentHashMap<>(32);


private static JAXBContext instanceJAXBContext(Class<?> cls) throws JAXBException {
		JAXBContext jc = JAXBCONTEXT_MAP.get(cls.getName());
		if (Objects.isNull(jc)) {
			jc = JAXBContext.newInstance(cls);
			JAXBCONTEXT_MAP.put(cls.getName(), jc);
		}
		return jc;
	}

转载来源:https://juejin.cn/post/7385245216053542946

相关文章
|
Java Android开发
jvisualvm分析jvm内存溢出
jvisualvm分析jvm内存溢出
2049 0
jvisualvm分析jvm内存溢出
|
存储 关系型数据库 数据库
聊多版本并发控制(MVCC)
MVCC是数据库并发控制技术,用于减少读写冲突。它维护数据的多个版本,使事务能读旧数据而写新数据,无需锁定记录。当前读获取最新版本,加锁防止修改;快照读不加锁,根据读取时的读视图(readview)决定读哪个版本。InnoDB通过隐藏字段(DB_TRX_ID, DB_ROLL_PTR)和undo log存储版本,readview记录活跃事务ID。读已提交每次读取都创建新视图,可重复读则在整个事务中复用一个视图,确保一致性。MVCC通过undo log版本链和readview规则决定事务可见性,实现了非阻塞并发读。
1867 5
聊多版本并发控制(MVCC)
|
消息中间件 存储 RocketMQ
Rocketmq如何保证消息不丢失
文章分析了RocketMQ如何通过生产者端的同步发送与重试机制、Broker端的持久化存储与消息重试投递策略、以及消费者端的手动提交ack与幂等性处理,来确保消息在整个传输和消费过程中的不丢失。
|
12月前
|
监控 Java 测试技术
OOM排查之路:一次曲折的线上故障复盘
本文分享了在整合Paimon数据湖与RocksDB过程中,因内存溢出(OOM)引发的三次线上故障排查过程。通过SDK进行数据读写时,系统连续出现线程数突增、内存泄漏等问题,排查过程涉及堆内与堆外内存分析、JNI内存泄漏定位及架构优化。最终通过调整bucket数量、优化JVM参数及采用Flink写入Paimon,成功解决问题。文中详述了使用MAT、NMT、Arthas、async-profiler等工具的实战经验,为使用类似技术栈的开发者提供参考。
1226 17
OOM排查之路:一次曲折的线上故障复盘
|
10月前
|
人工智能 负载均衡 应用服务中间件
Dify 性能瓶颈?Higress AI 网关为它注入「高可用之魂」!
Dify 是一款开源 AI 应用开发平台,因其灵活的工作流编排和易用性受到广泛关注,但在用户规模扩大和生产落地过程中,逐渐暴露出性能瓶颈,影响系统稳定性。本文介绍如何通过 Higress AI 网关提升 Dify 应用的全链路高可用性,并提供详细操作指南。AI 网关具备多维度限流、Token 级控制、模型 Fallback、负载均衡等能力,有效保障 Dify 应用在高并发场景下的稳定运行。
1306 1
|
网络协议 Java
【Java】已解决java.net.UnknownHostException异常
【Java】已解决java.net.UnknownHostException异常
5194 0
|
XML JSON Java
Java中Log级别和解析
日志级别定义了日志信息的重要程度,从低到高依次为:TRACE(详细调试)、DEBUG(开发调试)、INFO(一般信息)、WARN(潜在问题)、ERROR(错误信息)和FATAL(严重错误)。开发人员可根据需要设置不同的日志级别,以控制日志输出量,避免影响性能或干扰问题排查。日志框架如Log4j 2由Logger、Appender和Layout组成,通过配置文件指定日志级别、输出目标和格式。
|
Java 数据库连接 mybatis
mybatis注解@Select中添加判断条件<script>
mybatis注解@Select中添加判断条件<script>
1480 0
|
Oracle 关系型数据库 Windows
如何彻底卸载 IDEA,将IDEA完全删除
本文提供了一个详细的教程,指导用户如何彻底卸载 IntelliJ IDEA,包括使用Uninstall.exe程序和通过注册表删除残留项的步骤。
12306 3
如何彻底卸载 IDEA,将IDEA完全删除
|
前端开发 安全 JavaScript
SpringBoot 如何解决跨域问题?
本文深入探讨了Spring Boot解决跨域问题的方法,包括全局配置CORS、使用@CrossOrigin注解和自定义过滤器,提供了详细的代码示例和分析,帮助开发者有效应对Web开发中的跨域挑战。
2274 4