Java 调试入门工具
jps
jps是jdk提供的一个查看当前java进程的小工具, 可以看做是JavaVirtual Machine Process Status Tool的缩写。
jps常用命令
jps参数
jps原理
java程序在启动以后,会在java.io.tmpdir指定的目录下,就是临时文件夹里,生成一个类似于hsperfdata_User的文件夹,这个文件夹里(在Linux中为/tmp/hsperfdata_{userName}/),有几个文件,名字就是java进程的pid,因此列出当前运行的java进程,只是把这个目录里的文件名列一下而已。 至于系统的参数什么,就可以解析这几个文件获得。
更多请参考 jps - Java Virtual Machine Process Status Tool
jstack
jstack是jdk自带的线程堆栈分析工具,使用该命令可以查看或导出 Java 应用程序中线程堆栈信息。线程快照是当前虚拟机内每一条线程上在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、 请求外部资源导致的长时间等待等问题
注意:
Jstack 可以直接检测死锁;
Jstack 并不能直接检测死循环,但可以通过分析线程堆栈信息间接发现死循环的存在;例如
线程一直处于
RUNNABLE状态。CPU 使用率异常高(可以通过
top或pidstat等工具查看)。线程的堆栈信息中会反复出现相同的函数调用。
jstack常用命令:
pid 是需要被打印配置信息的java进程id,可以用jps查询
jstack参数:
Jstack 使用
通过使用 jps 命令获取需要监控的进程的pid,然后使用 jstack pid 命令查看线程的堆栈信息。
通过 jstack 命令可以获取当前进程的所有线程信息。
每个线程堆的信息中,都可以查看到 线程ID、线程的状态(wait、sleep、running 等状态)、是否持有锁信息等。
死锁示例
下面通过一个例子,来演示 jstack 检查死锁的一个例子,代码如下:
使用 jstack -l pid 查看线程堆栈信息,发现在堆栈信息最后面检查出了一个死锁。如下图
可以清楚的看出 mythread2 等待 这个锁 “0x00000000d6eb82d0”,这个锁是由于mythread1线程持有。
mythread1线程等待这个锁“0x00000000d6eb8300”,这个锁是由mythread2线程持有。
“mythread1”线程堆栈信息如下:
可以看出当前线程持有“0x00000000d6eb82d0”锁,等待“0x00000000d6eb8300”的锁
“mythread2”线程堆栈信息如下:
“mythread2”的堆栈信息中可以看出当前线程持有“0x00000000d6eb8300”锁,等待“0x00000000d6eb82d0”的锁。
jinfo
jinfo 是 JDK 自带的命令,可以用来查看正在运行的 java 应用程序的扩展参数,包括Java System属性和JVM命令行参数;也可以动态的修改正在运行的 JVM 一些参数。当系统崩溃时,jinfo可以从core文件里面知道崩溃的Java应用程序的配置信息
Javacore,也可以称为“threaddump”或是“javadump”,它是 Java 提供的一种诊断特性,能够提供一份可读的当前运行的 JVM 中线程使用情况的快照。即在某个特定时刻,JVM 中有哪些线程在运行,每个线程执行到哪一个类,哪一个方法。应用程序如果出现不可恢复的错误或是内存泄露,就会自动触发 Javacore 的生成。
jinfo常用命令:
jinfo参数:
示例一: no option
命令:jinfo pid
描述:输出当前 jvm 进程的全部参数和系统属性

示例二: -flag name
命令:jinfo -flag name pid
描述:输出对应名称的参数
使用该命令,可以查看指定的 jvm 参数的值。如:查看当前 jvm 进程是否开启打印 GC 日志。
示例三:-flag [+|-]name
命令:jinfo -flag [+|-]name pid 描述:开启或者关闭对应名称的参数
使用 jinfo 可以在不重启虚拟机的情况下,可以动态的修改 jvm 的参数。尤其在线上的环境特别有用。
使用如下:

示例四:-flag name=value
命令:jinfo -flag name=value pid 描述:修改指定参数的值。
同示例三,但示例三主要是针对 boolean 值的参数设置的。 如果是设置 value值,则需要使用 name=value 的形式。
使用如下:

注意事项 :jinfo虽然可以在java程序运行时动态地修改虚拟机参数,但并不是所有的参数都支持动态修改
示例五: -flags
命令:jinfo -flags pid
描述:输出全部的参数

示例六:-sysprops
命令:jinfo -sysprops pid
描述:输出当前 jvm 进行的全部的系统属性

jmap
命令jmap是一个多功能的命令。它可以生成 java 程序的 dump 文件, 也可以查看堆内对象示例的统计信息、查看 ClassLoader 的信息以及 finalizer 队列。
两个用途
jmap参数
示例一:no option
命令:jmap pid 描述:查看进程的内存映像信息,类似 Solaris pmap 命令。
使用不带选项参数的jmap打印共享对象映射,将会打印目标虚拟机中加载的每个共享对象的起始地址、映射大小以及共享对象文件的路径全称。这与Solaris的pmap工具比较相似。

示例二:heap
命令:jmap -heap pid 描述:显示Java堆详细信息
打印一个堆的摘要信息,包括使用的GC算法、堆配置信息和各内存区域内存使用信息
示例三:histo[:live]
命令:jmap -histo:live pid 描述:显示堆中对象的统计信息
其中包括每个Java类、对象数量、内存大小(单位:字节)、完全限定的类名。打印的虚拟机内部的类名称将会带有一个’*’前缀。如果指定了live子选项,则只计算活动的对象。

示例四:clstats
命令:jmap -clstats pid 描述:打印类加载器信息
-clstats是-permstat的替代方案,在JDK8之前,-permstat用来打印类加载器的数据
打印Java堆内存的永久保存区域的类加载器的智能统计信息。对于每个类加载器而言,它的名称、活跃度、地址、父类加载器、它所加载的类的数量和大小都会被打印。此外,包含的字符串数量和大小也会被打印。

示例五:finalizerinfo
命令:jmap -finalizerinfo pid
描述:打印等待终结的对象信息
Number of objects pending for finalization: 0 说明当前F-QUEUE队列中并没有等待Fializer线程执行final
示例六:dump:<dump-options>
命令:jmap -dump:format=b,file=heapdump.phrof pid 描述:生成堆转储快照dump文件。
以hprof二进制格式转储Java堆到指定filename的文件中。live子选项是可选的。如果指定了live子选项,堆中只有活动的对象会被转储。想要浏览heap dump,你可以使用jhat(Java堆分析工具)读取生成的文件。
这个命令执行,JVM会将整个heap的信息dump写入到一个文件,heap如果比较大的话,就会导致这个过程比较耗时,并且执行的过程中为了保证dump的信息是可靠的,所以会暂停应用, 线上系统慎用。
更多请参考: jmap - Memory Map
注意
此命令会导致虚拟机暂停工作1~3秒,因此在实际生产环境中基本不使用!!!一般情况下,在出现问题时,会用以下方案
服务已OOM或即将OOM:依赖预设的JVM参数
-XX:+HeapDumpOnOutOfMemoryError。这样JVM会在崩溃时自动生成Dump,无需手动干预,避免二次伤害。服务可用但性能劣化:Arthas,用它进行在线、低侵入式分析。
jstat
它是 JDK 自带的工县,用于监控JVM 各种运行时信息
jstat参数众多,但是使用一个就够了
gc选项:显示垃圾收集信息(也可以用 gcutil,gcutil以百分比形式显示内存的使用情况,gc显示的是内存占用的字节数,以KB 的形式输出堆内存的使用情况)
pid:Java 进程的 PID。
1000:每 1000 毫秒采样一次。
10:采样 10 次。
示例输出
字段含义:
S0C(Survivor Space 0 Capacity):第一个 Survivor 区域的容量(字节数).
S1C(Survivor Space 1 Capacity):第二个 Survivor 区域的容量(字节数)。
S0U(Survivor Space 0 Utilization):第一个 Survivor 区域的使用量(字节数)
S1U(Survivor Space 1 Utilization):第二个 Survivor 区域的使用量(字节数)。
EC(Eden Space Capacity): Eden 区域的容量(字节数)。
EU(Eden Space Utilization): Eden 区域的使用量(字节数)
OC(Old Generation Capacity): 老年代的容量(字节数)
OU(Old Generation Utilization): 老年代的使用量(字节数)
MC(Metaspace Capacity):方法区(Metaspace)的容量(字节数)
MU (Metaspace Utilization):方法区的使用量(字节数)。
CCSC(Compressed Class Space Capacity): 压缩类空间的容量(字节数)
CCSU(Compressed Class Space Utilization): 压缩类空间的使用量(字节数)
YGC (Young Generation GC Count):年轻代垃圾回收的次数
YGCT (Young Generation GC Time):年轻代垃圾回收的总时间(秒)。
FGC (Full GC Count): full gc 的次数。
FGCT(Full GC Time): full gc 的总时间(秒)。
GCT(Garbage Collection Time): 总的垃圾回收时间(秒)。
注意:如果 FGC 变化频率很高,则说明系统性能和吞吐量将下降,或者可能出现内存溢出。
jdb
jdb可以用来预发debug,假设你预发的java_home是/opt/java/,远程调试端口是8000.那么
出现以上代表jdb启动成功。后续可以进行设置断点进行调试。
具体参数可见oracle官方说明jdb - The Java Debugger
CHLSDB
CHLSDB感觉很多情况下可以看到更好玩的东西,不详细叙述了。 查询资料听说jstack和jmap等工具就是基于它的。
更详细的可见R大此贴
Java 调试进阶工具
btrace
首当其冲的要说的是btrace。真是生产环境&预发的排查问题大杀器。 简介什么的就不说了。直接上代码干
l 查看当前谁调用了ArrayList的add方法,同时只打印当前ArrayList的size大于500的线程调用栈
- 监控当前服务方法被调用时返回的值以及请求的参数
btrace 具体可以参考这里:https://github.com/btraceio/btrace
注意:
经过观察,1.3.9的release输出不稳定,要多触发几次才能看到正确的结果
正则表达式匹配trace类时范围一定要控制,否则极有可能出现跑满CPU导致应用卡死的情况
由于是字节码注入的原理,想要应用恢复到正常情况,需要重启应用。
Greys
Greys是@杜琨的大作吧。说几个挺棒的功能(部分功能和btrace重合):
sc -df xxx: 输出当前类的详情,包括源码位置和classloader结构
trace class method: 打印出当前方法调用的耗时情况,细分到每个方法, 对排查方法性能时很有帮助。
Arthas
Arthas是基于Greys。
输入 dashboard 命令,按回车 enter,会展示当前进程的信息,按 ctrl+c 可以中断执行
字段含义:
heap: 堆内存的使用情况:
used 32M: 当前堆内存使用 32MB
total 155M: 堆内存总量为 155MB.
max1820M:堆内存最大量为1820MB.
usage 1.77%: 堆内存使用百分比为 1.77%
ps_eden_space: 年轻代 Eden 区域的使用情况
used 14M: 当前 Eden 区域使用 14MB
total 65M: Eden 区域总量为 65MB.
max 672M: Eden 区域最大量为 672MB.
usage 2.21%: Eden 区域使用百分比为 2.21%。
ps_survivor_space: 年轻代 Survivor 区域的使用情况。
used 4M: 当前 Survivor 区域使用 4MB.
total 5M: Survivor 区域总量为 5MB。
max 5M: Survivor 区域最大量为 5MB。
ps_old_gen: 老年代的使用情况,
used 12M: 当前老年代使用12MB
total 85M: 老年代总量为 85MB。
max 1365M: 老年代最大量为1365MB.
usage 0.91%: 老年代使用百分比为 0.91%
nonheap: 非堆内存的使用情况。
used 20M: 当前非堆内存使用 20MB.
total 23M: 非堆内存总量为 23MB。
code cache: 代码缓存区的使用情况
used 3M: 当前代码缓存区使用 3MB.
total 5M: 代码缓存区总量为 5MB。
max 240M: 代码缓存区最大量为 240MB。
usage 1.32%: 代码缓存区使用百分比为 1.32%。
javOSize
就说一个功能:
- classes:通过修改了字节码,改变了类的内容,即时生效。 所以可以做到快速的在某个地方打个日志看看输出,缺点是对代码的侵入性太大。但是如果自己知道自己在干嘛,的确是不错的玩意儿。
其他功能Greys和btrace都能很轻易做的到,不说了。
更多请参考:官网
JProfiler
之前判断许多问题要通过JProfiler,但是现在Greys和btrace基本都能搞定了。再加上出问题的基本上都是生产环境(网络隔离),所以基本不怎么使用了,但是还是要标记一下。
更多请参考:官网
其它工具
dmesg
如果发现自己的java进程悄无声息的消失了,几乎没有留下任何线索,那么dmesg一发,很有可能有你想要的。
sudo dmesg|grep -i kill|less 去找关键字oom_killer。找到的结果类似如下:
以上表明,对应的java进程被系统的OOM Killer给干掉了,得分为854. 解释一下OOM killer(Out-Of-Memory killer),该机制会监控机器的内存资源消耗。当机器内存耗尽前,该机制会扫描所有的进程(按照一定规则计算,内存占用,时间等),挑选出得分最高的进程,然后杀死,从而保护机器。
dmesg日志时间转换公式: log实际时间=格林威治1970-01-01+(当前时间秒数-系统启动至今的秒数+dmesg打印的log时间)秒数:
date -d "1970-01-01 UTC echo "$(date +%s)-$(cat /proc/uptime|cut -f 1 -d' ')+12288812.926194"|bc seconds" 剩下的,就是看看为什么内存这么大,触发了OOM-Killer了。
JVM可视化工具
JConsole
Jconsole (Java Monitoring and Management Console),JDK自带的基于JMX的可视化监视、管理工具。 官方文档可以参考这里在新窗口打开
- 找到jconsole工具
-l 打开jconsole
选择


- 查看概述、内存、线程、类、VM概要、MBean
概述
内存
线程
类
VM概要
MBean

Visual VM
VisualVM 是一款免费的,集成了多个 JDK 命令行工具的可视化工具,它能为您提供强大的分析能力,对 Java 应用程序做性能分析和调优。这些功能包括生成和分析海量数据、跟踪内存泄漏、监控垃圾回收器、执行内存和 CPU 分析,同时它还支持在 MBeans 上进行浏览和操作。
Overview
Monitor
线程
Sampler

Visual GC
visual gc 是 visualvm 中的图形化查看 gc 状况的插件。官方文档可以参考这里
比如我在IDEA中使用visual GC 插件来看GC状况。

JProfile
JProfiler 是一个商业的主要用于检查和跟踪系统(限于Java开发的)的性能的工具。JProfiler可以通过时时的监控系统的内存使用情况,随时监视垃圾回收,线程运行状况等手段,从而很好的监视JVM运行情况及其性能。
JProfiler 是一个全功能的Java剖析工具(profiler),专用于分析J2SE和J2EE应用程序。它把CPU、执行绪和内存的剖析组合在一个强大的应用中。 JProfiler可提供许多IDE整合和应用服务器整合用途。JProfiler直觉式的GUI让你可以找到效能瓶颈、抓出内存漏失(memory leaks)、并解决执行绪的问题。它让你得以对heap walker作资源回收器的root analysis,可以轻易找出内存漏失;heap快照(snapshot)模式让未被参照(reference)的对象、稍微被参照的对象、或在终结(finalization)队列的对象都会被移除;整合精灵以便剖析浏览器的Java外挂功能。
核心组件
JProfiler 包含用于采集目标 JVM 分析数据的 JProfiler agent、用于可视化分析数据的 JProfiler UI、提供各种功能的命令行工具,它们之间的关系如下图所示。

- JProfiler agent:
JProfiler agent 是一个本地库,它可以在 JVM 启动时通过参数-agentpath:<path to native library>进行加载或者在程序运行时通过JVM Attach 机制进行加载。Agent 被成功加载后,会设置 JVMTI 环境,监听虚拟机产生的事件,如类加载、线程创建等。例如,当它监听到类加载事件后,会给这些类注入用于执行度量操作的字节码。
- JProfiler UI:JProfiler UI 是一个可独立部署的组件,它通过 socket 和 agent 建立连接。这意味着不论目标 JVM 运行在本地还是远端,JProfiler UI 和 agent 间的通信机制都是一样的。
JProfiler UI 的主要功能是展示通过 agent 采集上来的分析数据,此外还可以通过它控制 agent 的采集行为,将快照保存至磁盘,展示保存的快照。
- 命令行工具:JProfiler 提供了一系列命令行工具以实现不同的功能。
jpcontroller - 用于控制 agent 的采集行为。它通过 agent 注册的 JProfiler MBean 向 agent 传递命令。
jpenable - 用于将 agent 加载到一个正在运行的 JVM 上。
jpdump - 用于获取正在运行的 JVM 的堆快照。
jpexport & jpcompare - 用于从保存的快照中提取数据并创建 HTML 报告。
运行测试
运行一个SpringBoot测试工程,选择attach到JVM
选择指定的进程
设置数据采集模式
JProfier 提供两种数据采集模式 Sampling 和 Instrumentation。
Sampling - 适合于不要求数据完全精确的场景。优点是对系统性能的影响较小,缺点是某些特性不支持(如方法级别的统计信息)。
Instrumentation - 完整功能模式,统计信息也是精确的。缺点是如果需要分析的类比较多,对应用性能影响较大。为了降低影响,往往需要和 Filter 一起使用。
由于我们需要获取方法级别的统计信息,这里选择了 Instrumentation 模式。
概览
内存
实时内存分布(类对象)
dump 堆内存
dump完会直接打开显示
线程存储
导出HTML报告
CPU 调用树
线程历史
JEE & 探针
MBeans

Eclipse Memory Analyzer (MAT)
MAT 是一种快速且功能丰富的 Java 堆分析器,可帮助你发现内存泄漏并减少内存消耗。 MAT在的堆内存分析问题使用极为广泛,需要重点掌握。
- Overview:包含内存分布,以及潜在的问题推测

- Histogram:可以列出内存中的对象,对象的个数以及大小。
具体需要重点理解如下两个概念,可参考官网文档的解释
Shallow Heap :一个对象内存的消耗大小,不包含对其他对象的引用
Retained Heap :是shallow Heap的总和,也就是该对象被GC之后所能回收到内存的总和
- Dominator Tree:可以列出那个线程,以及线程下面的那些对象占用的空间。

- Top consumers:通过图形列出最大的object。

- Leak Suspects:自动分析潜在可能的泄漏。

GCeasy
GCeasy,它是一个分析 GC 日志文件的在线网站,能根据上传的 GC 日志,以图表形式分析 GC 情况:
直接在主页上传堆转储文件即可,可以得到 GC的分析结果