bingmous

欢迎交流,不吝赐教~

导航

JDK诊断工具使用总结

以下使用的是jdk11

jps 查看正在运行的java进程

jps(JVM Process Status Tool)列出正在运行的虚拟机进程

使用示例

jps
jps -l
jps -lvm  # l显示主类全名 v显示jvm参数 m显示main方法参数

jstat 查看jvm运行的一些统计信息

jstat(JVM Statistics Monitoring Tool)虚拟机统计信息监视工具


基本使用:jstat -选项 -t -h 5 进程号 刷新间隔 刷新次数

  • 统计选项可通过jstat -option 可以查看可选的所有选项
  • -t,表示显示进程启动后运行的时间
  • -h,表示几行显示以下标题,比如-h 3,则每打印三行就会显示一下标题,

使用示例

jstat -class 8048  # 查看类加载情况
jstat -gcutil -t 8048 1s 5  # -t显示jvm运行时间 每秒输出一次 输出5次

# JIT相关的
jstat -compiler PID  # 显示JIT编译过的方法、耗时等信息
jstat -printcompilation  # 显示已经被JIT编译的方法

经验:

  • 在使用gcutil时,可以通过查看一段时间内,gc总时间差值占用jvm运行时间差值的比例,如果超过20%,说明堆的压力比较大,如果超过90%,说明堆里几乎没有可用空间,随时都有可能发生OOM
  • 使用gc时,可以通过每隔一段时间查看老年代(OU列)的大小,如果一直是增加的,说明可能有内存泄漏

jinfo 查看或修改jvm的参数、系统属性

jinfo(Configuration Info for Java)Java配置信息工具,实时查看和调整虚拟机各项参数。jdk9之后集成到了jhsdb里面

使用示例:

# 查看
jinfo 23167		# 打印 VM flags(赋过值的) and system properties,等同于以下两项
jinfo -flags 23167	 # 打印 VM flags,赋过值的VM参数
jinfo -sysprops 23167	# 打印 system properties,在程序中也可以通过System.getProperties()获取
jinfo -flag MetaspaceSize 23167	# 打印指定的vm参数值,如MetaspaceSize
# 修改
jinfo -flag [+|-]<name> 23167	# 开启、关闭某个vm(boolean类型)参数
jinfo -flag <name>=<value>	# 设置某个vm(kv类型)参数

jinfo不仅可以查看运行时参数,也可以修改部分参数,使其立即生效(只有参数被标记为manageable的flag可以实时修改,修改能力及其有限)

  • java -XX:+PrintFlagsFinal -version | grep manageable查看哪些可以实时修改
  • java -XX:+PrintFlagsInitial,查看JVM参数启动时的初始值
  • java -XX:+PrintFlagsFinal,查看JVM参数的最终值
  • java -XX:+PrintCommandLineFlags,查看已经被用户或者JVM设置过的详细的XX参数参数的名称和值

jmap 导出内存映像文件和内存使用情况

jmap(Memory Map for Java)Java内存映像工具,用于生成堆转储快照,它还可以查询finalize执行队列、Java堆和方法区的详细信息,如空间使用率、当前用的是哪种收集器等

使用示例:

jmap -dump:live,format=b,file=fileName.hprof PID  # dump出堆内存快照,live表示只dump活着的对象,在堆比较大时,hprof文件可能会比较大
jmap -heap PID  # 显示堆内存相关信息,只显示命令执行那一刻的
jmap -histo PID  # 显示堆中类的实例数量、占用内存统计信息

一般在快OOM时提前dump,或者开启OOM自动dump,使用dump文件分析哪些对象导致OOM

  • -XX:+HeapDumpOnOutOfMemoryError,OOM前dump
  • -XX:HeapDumpBeforeFullGC,fullGC前dump
  • -XX:HeapDumpPath=<fileName.hprof>,指定文件路径,不指定则在当前目录下生成

说明:由于jmap需要访问堆中的所有对象,为了保证此过程不被应用程序干扰,jmap需要借助安全点机制,让所有线程留在不改变堆中数据的状态,也即,jmap导出的堆快照必定是在安全点位置的,这可能导致基于该堆快照的分析结果存在偏差。

比如在编译生成的机器码中,某些对象的生命周期在两个安全点之间,那么jmap live选项将无法探知到这些对象。另外,如果线程长时间无法跑到安全点,jmap将一直等下去,与jstat不同,垃圾收集器会主动将jstat需要的数据保存至固定的位置,jstat只需要直接读取即可

jhat 分析jmap导出的堆内存快照

jhat(JVM Heap Analysis Tool):虚拟机堆转储快照分析工具,JDK提供jhat命令与jmap搭配使用,来分析jmap生成的堆转储快照。(一般不用,一般拷贝快照到别的服务器使用工具进行分析)

jhat内置了一个微型的http/html服务器,生成dump文件的分析结果后,可以在浏览器http://localhost:7000 查看分析结果

jhat命令在jdk9、jdk10中已经被删除,官方建议使用visualvm代替

使用示例:

jhat xxx.hprof  # 分析堆内存快照,一般不在生产服务器使用

jstack 查看线程堆栈

jstack(Stack Trace for Java)Java堆栈跟踪工具,用于生成虚拟机当前时刻的线程快照。

从JDK 5起,java.lang.Thread类新增了一个getAllStackTraces()方法用于获取虚拟机中所有线程的StackTraceElement对象。使用这个方法可以通过简单的几行代码完成jstack的大部分功能。jdk9之后集成到了jhsdb里面

使用示例:

jstack PID  # 查看线程堆栈
jstack -l -e PID  # -l表示显示锁信息,-e表示显示额外的信息(allocated、defined_classes)

生成线程快照的作用:可用于定位线程执行长时间停顿的原因,如线程死锁、死循环、请求外部资源导致长时间等待等问题,这些都是导致线程长时间停顿的常见原因,当线程长时间停顿时,就可以用jstack显示各个线程调用的堆栈情况。

一般将结果输出到一个文件里,分析文件

...

输出说明:

  • 第一部分是jvm的信息,堆栈快照的时间、虚拟机版本模式等信息
  • 第二部分是jvm内部线程信息(SMR为Safe Memory Reclamation安全内存分配),这里的elements与下面的各个堆栈信息的tid对应,堆栈中的nid与外部操作系统查看到的线程id对应
  • 第三部分是线程的具体信息,依次为:
    • 线程名称
    • 线程id,demon表示是守护线程
    • 线程的优先级
    • 操作系统中线程的优先级
    • cpu,线程获取到的cpu总时间
    • elaspsed,线程启动后经过的时间
    • allocated,线程分配的字节数
    • defined_classes,本线程定义的class个数(要开启 -XX:+PrintExtendedThreadInfo才会输出数据)
    • tid,线程id,与SMR中elements中的值对应
    • nid,操作系统中线程id,这里显示的都是十六进制的
    • 线程的状态,waiting on condition
    • 下面就是线程具体的堆栈了
    • Locked ownable synchronizers:表示线程可用的用于同步的排它锁对象,Ownable Synchronizer是一个同步器,这个同步器的同步属性是通过使用AbstractOwnableSynchronizer或者它的子类来实现的,如ReentrantLock和ReentrantReadWriteLock中的write-lock
  • 第四部分是JVM的线程信息
  • 最后一部分是JNI(Java Native Interface)引用的信息,注意这些引用可能会导致内存泄露,因为这些native的引用并不会被自动垃圾回收

排除线程cpu过高

可用通过top -p PID -H查出线程高的id,转换成16进制,在jstack中根据nid找到该线程的堆栈进行分析可能导致cpu过高的代码

排除死锁

分析jstack里面是否有DeadLock关键字

jcmd 多功能工具

在jdk1.7之后,新增的命令行工具jcmd,它是一个多功能工具,可以用来实现除了jstat之外的所有命令的功能,比如:导出堆、内存使用、查看java进程、导出线程信息、执行GC、JVM运行时间等

jcmd拥有jmap大部分的功能,并且官网推荐使用jcmd代替jmap

jcmd  # 列出所有的jvm进程,类似jps
jcmd PID help  # 查看针对当前进程支持的所有命令
jcmd PID 命令选项  # 执行上面支持的命令

#比如
jcmd PID Thread.print  # 打印线程堆栈,可以替换jstack
jcmd PID GC.class_histogram  # 打印对象统计信息,可以替换jmap -histo
jcmd PID GC.heap_dump fileName  # dump堆内存快照,可以替换jmap -dump
jcmd PID VM.uptime  # 查看进程启动的时间,可以替换jstat中的-t功能
jcmd PID VM.system_properties  # 查看系统属性,可以替换jinfo -sysprops
jcmd PID VM.flags  # 查看JVM的一些参数信息,可以替换jinfo -flags

jstatd 远程主机信息收集

jstatd是一个RMI服务端程序,它的作用相当于代理服务器,建立本地计算机与远程监控工具的通信,jstatd服务器将本机的java应用程序信息传递到远程计算机。

jhsdb

jdk9开始提供,集成了jstatd、jinfo、jmap、jhat、jstack

有很多子选项,增加对应的选项后可以查看具体命令帮助

查看jvm堆状态

jhsdb jmap --heap --pid PID

-class 查看类加载统计

输出说明: - 时间戳 - Loaded,加载的class数量 - Bytes,所占空间大小 - Unloaded,未加载数量 - Bytes,未加载所占空间大小 - Time,加载使用的时间

-compiler 查看编译统计

输出说明: - Compiled:编译数量 - Failed:失败数量 - Invalid:不可用数量 - Time:时间 - FailedType:失败类型 - FailedMethod:失败的方法

-gc 查看垃圾回收统计

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

-gccapacity 查看堆内存统计

输出说明: - NGCMN:新生代最小容量 - NGCMX:新生代最大容量 - NGC:当前新生代容量 - S0C:第一个幸存区大小 - S1C:第二个幸存区的大小 - EC:伊甸园区的大小 - OGCMN:老年代最小容量 - OGCMX:老年代最大容量 - OGC:当前老年代大小 - OC:当前老年代大小 - MCMN:最小元数据容量 - MCMX:最大元数据容量 - MC:当前元数据空间大小 - CCSMN:最小压缩类空间大小 - CCSMX:最大压缩类空间大小 - CCSC:当前压缩类空间大小 - YGC:年轻代gc次数 - FGC:老年代GC次数

-gccause 查看gc原因

jstat -gccause -t 8048 1s 5

元空间内存统计,新生代、老年代内存及垃圾回收统计

jstat -gcmetacapacity -t 8048 1s 5
jstat -gcnew -t 8048 1s 5
jstat -gcnewcapacity -t 8048 1s 5
jstat -gcold -t 8048 1s 5
jstat -gcoldcapacity -t 8048 1s 5

-gcutil 垃圾回收总结统计

输出说明: - S0:幸存1区当前使用比例 - S1:幸存2区当前使用比例 - E:伊甸园区使用比例 - O:老年代使用比例 - M:元数据区使用比例 - CCS:压缩使用比例 - YGC:年轻代垃圾回收次数 - YGCT:年轻代垃圾回收时间,从启动到当前时间,单位s - FGC:老年代垃圾回收次数 - FGCT:老年代垃圾回收消耗时间,从启动到当前时间,单位s - CGC:压缩类空间垃圾回收次数 - CGCT:压缩类空间垃圾回收总时间 - GCT:垃圾回收消耗总时间,从启动到当前时间,单位s

-printcompilation 查看jvm编译方法统计

输出说明: - Compiled:最近编译方法的数量 - Size:最近编译方法的字节码数量 - Type:最近编译方法的编译类型 - Method:方法名标识

jconsole

jdk自带的可视化监控工具,查看java应用程序的运行概况、监控堆信息、永久代(元空间)使用情况及类加载情况。

jdk1.5开始,用于对JVM中内存、线程和类等的监控,是一个基于JMX(Java Management Extensions)的GUI性能监控工具

直接双击jdk/bin/jconsole.exe即可,可以本地连接、远程连接,远程连接需要在环境变量中设置mx.remote.credentials指定用户名和密码,从而进行授权。

可以查看jinfo、jstack、jstat、jmap等命令的信息,提供了简单的曲线图

jvisualvm

查看jvm应用程序的详细信息,是一个功能强大的多合一的故障诊断和性能监控的可视化工具。

它集成了多个jdk命令行工具,使用visualvm可用于显示虚拟机进程及进程的配置和环境信息(jps、jinfo),监控应用程序的cpu、GC、堆、方法区及线程的信息(jstat、jstack)等,甚至代替jconsole。

在jdk6u7之后,visualvm便作为jdk的一部分发布,也可以作为独立的软件安装。支持插件扩展,并且安装插件非常方便。可以离线下载*.nbm,然后在plugin对话框的已下载页面上添加,也可以在线安装。(建议安装visualGC),插件地址:https://visualvm.github.io/pluginscenters.html ,idea中也可以安装visualvm launcher插件,配置后会自动调用jdk的jvisualvm

jdk9及以上不再自带visualVM,需要单独下载:http://visualvm.github.io/download.html

主要功能:

  • 生成、读取堆内存快照
  • 查看jvm参数和系统属性
  • 查询运行中的虚拟机进程
  • 生成、读取线程快照
  • 程序资源的实时监控
  • 其他功能:jmx代理连接、远程环境监控、cpu分析和内存分析

安装:

  • 官网下载
  • 解压后修改配置文件visualvm.conf,修改为使用的jdk路径

Memory Analyzer Tool(MAT)

基于Eclipse的内存分析工具,是一个快速、功能丰富的Java堆分析工具,它可以帮助我们查找内存泄漏和减少内存消耗。

eclipse插件,也有独立软件

主要用于分析堆内存快照:

  • 所有的对象信息,包括实例变量、成员变量、存储于栈中的基本类型值和存储于堆中的其他对象的引用值
  • 所有的类信息,包括ClassLoader、类名称、父类、静态变量等
  • GCRoots到所有这些对象的引用路径
  • 线程信息,包括线程的调用栈及此线程的线程局部变量(TLS)

MAT最吸引人的地方是能够快速的为开发人员生成内存泄漏报表,方便定位问题和分析问题。虽然MAT有如此强大的功能,但是内存分析也没有简单到一键完成的程度,很多内存问题还是需要我们从MAT展现给我们的信息当中通过经验和直觉判断才能发现。

实际一般采用jmap + MAT进行离线分析。

使用概念:

  • 保留集(retained set),对象a的保留集指当对象a被垃圾回收后,可以被释放的所有的对象的集合(包括对象a),及对象a的保留集是只能通过对象a被直接或间接访问到的所有对象的集合。通俗的说,仅被对象a所持有的对象的集合。
  • shallow heap:浅堆,表示一个对象所消耗的内存大小,不包括对象中的引用类型的对象的大小
  • retained heap:深堆,当前对象,及当前对象的所有引用对象的大小,即对象的保留集中所有对象的浅堆大小之和。
  • 对象的实际大小:一个对象所能触及的所有对象的浅堆大小之和,也就是通常意义上的对象大小。与深堆相比,这个在日常开发中更为直观,但实际上,这个概念和垃圾回收无关。
  • 支配树(Dominator Tree):支配树体现了对象之间的支配关系,在对象的引用图中,所有指向对象B的路径都经过对象A,则认为对象A支配对象B。如果对象A是离对象B最近的一个支配对象,则认为对象A是对象B的直接支配者。支配树是基于对象的引用图建立的,它有以下基本性质:
    • 对象A的子树(所有被对象A支配的对象的集合)表示对象A的保留集,即深堆
    • 如果对象A支配对象B,那么对象A的直接支配者也支配对象B
    • 支配树的边与对象引用图的边不直接对应
  • 深堆大小减去浅堆大小,就等于该对象中的支配对象的深堆大小之和,比如List,该对象的深堆大小减去该对象的浅堆大小(对象头、类型引用、数组长度、对齐),就等于List中所有元素的深堆大小之和。

内存泄漏

严格来讲,只有对象不再使用了,但是GC又无法回收的情况,叫做内存泄漏。但是实际情况中,很多时候因为一些不太好的实践导致对象的生命周期过长,甚至导致OOM,也叫做宽泛意义上的内存泄漏。

内存泄漏的几种情况:

  • 静态集合类:如果容器为static的,它们的生命周期与JVM程序一致,则容器中的对象在程序结束之前不会释放,从而造成内存泄漏。也即是,长生命周期的对象持有短生命周期对象的引用,尽管短生命周期对象不再使用,但是因为长生命周期对象持有它的引用导致不能回收。
  • 单例模式:单例对象的生命周期与JVM程序一致,单例持有外部对象的引用,造成外部对象无法回收
  • 内部类持有外部类引用:一个外部类的实例对象返回了一个内部类的实例对象,这个内部类对象被长期引用了,那么即使这个外部类对象不再使用,但由于内部类对象持有外部类的实例对象,这个外部类对象也不会被垃圾回收,造成内存泄漏。
  • 各种连接,如数据库连接、网络连接和IO等,没有在finally中关闭。
  • 变量不合理的作用域
  • 改变哈希值:当一个对象被放进hashSet之后,就不能修改这个对象中参与计算哈希值的字段了,否则对象修改后的哈希值与最初存入hashSet中的哈希值就不同了,在这种情况下,即使使用contains方法使用该对象的当前引用作为参数在hashSet中检索对象,也无法返回对象的结果,这也导致无法从hashSet中单独删除当前对象
  • 缓存泄漏,如HashMap,可以使用WeakHashMap
  • 监听器和回调:如果客户端在你实现的api中注册回调,却没有显式的取消,那么就会积聚,需要确保回调立即被当做立即回收的最佳方法时只保存它的弱引用,例如将他们保持为WeakHashMap中的key

OQL(Object Query Language)

MAT支持一种类似SQL的OQL查询语言,可以在堆中进行对象的查找和筛选

select * from java.util.ArrayList  # 查询堆中的ArrayList对象
select v.elementData from java.util.ArrayList v  # 查询ArrayList对象中的elementData属性
select objects v.elementData from java.util.ArrayList v # 以对象的形式展示
select as retained set * from java.util.ArrayList  # 显示所有ArrayList对象的保留集
select * from 0xabcdef  # 查询指定地址的对象
select * from char[] s where s.@length > 10  # 查询长度大于10的char数组
select * from java.lang.String s where toString(s) like ".*java"  # 返回包含java字符串对象
select * from java.lang.String s where s.value != null  # 返回value不为null的字符串
# where支持多个条件的and or
select * from java.util.Vector v where v.elementData.@length > 15 and v.@retainedHeapSize > 1000  # 长度大于15且深堆大于1000的Vector
# 访问对象的属性
select toString(f.path.value) from java.io.File f  # 访问java.io.File对象的path属性并进一步访问value的值
select s.toString(), s.@objectId, s.objectAddress from java.lang.String s  # 查询String对象的内容
select v.elementData.@length from java.util.Vector v  # 查询内部数组的长度
select * from INSTANCEOF java.util.Vector  # 查询所有Vector及子类型

JProfiler

商业软件,需要付费,功能强大,与VisualVM类似,功能更强大。下载地址:https://www.ej-technologies.com/products/jprofiler/overview.html

主要功能:

  • 方法调用,对方法调用的分析可以帮你了解应用程序在做什么,并找到提高性能的方法
  • 内存分配,通过分析堆上对象、引用链和垃圾收集能帮你修复内存泄漏问题,优化内存使用
  • 线程和锁,提供各种针对线程和锁的分析视图,帮助发现多线程问题
  • 高级子系统,许多性能问题都发生在更高语义级别上,比如jdbc调用,可能需要找出执行最慢的sql语句,JProfiler支持对这些子系统进行集成分析

jprofiler13注册码:L-J12-STALKER#5846458-y8bdm6q8gtr7b#228a

数据采集方式:Instrumentation模式,重构字节码,对jvm有一定影响。Sampling,抽样模式

Athas

alibaba开源的Java诊断工具

arthas基于哪些工具实现:

  • 基于grey-anatomy二次开发
  • termd,命令行实现
  • crash,文本渲染
  • cli,命令行界面基于vert.x提供的cli库进行开发
  • compiler,内存编译器来源
  • apache common net,telnet client代码来源
  • javaagent,运行在main方法前的拦截器
  • asm,一个通用的java字节码操作和分析框架

使用:https://www.cnblogs.com/bingmous/p/15620831.html

  • 启动:java -jar arthas-boot.jar或者java -jar arthas-boot.jar PID
  • 查看帮助文档:java -jar arthas-boot.jar -h

Java Mission Control(JMC)

内置Java Flight Recorder(JFR),能够以极低的性能开销收集Java虚拟机的性能数据。

jdk11后开源

JFR是JMC的一个组件

其他工具

Flame Graphs(火焰图)

显示cpu消耗的瓶颈,横轴是时间,纵轴是调用栈,一个格子是一个栈帧

关于火焰图的讲解大部分来自https://www.brendangregg.com/flamegraphs.html ,

TProfiler

最重要的特性就是能够统计出指定时间内JVM的top method,这些top method极有可能是造成jvm性能的瓶颈

Btrace

Java运行时追踪工具,可以在不停机的情况下,跟踪指定的方法调用、构造函数和系统内存等信息。

动态调用应用程序的类以注入跟踪代码(字节码跟踪)

YourKit

JProbe

Spring Insight

总结

  • 查看堆栈:jstack -l -e PID
  • 查看堆内存:jhsdb jmap --heap --pid PID
  • dump堆:jmap -dump:live,format=b,file=./t.bin PID

posted on 2022-12-04 15:07  Bingmous  阅读(176)  评论(0编辑  收藏  举报