Java 线程调优 JDK常用命令行工具 Jstack & Arthas使用笔记 查找耗时线程

您所在的位置:网站首页 查看线程状态的工具是什么 Java 线程调优 JDK常用命令行工具 Jstack & Arthas使用笔记 查找耗时线程

Java 线程调优 JDK常用命令行工具 Jstack & Arthas使用笔记 查找耗时线程

2024-07-09 00:01| 来源: 网络整理| 查看: 265

toc

1. JDK 命令行工具

这些命令在 JDK 安装目录下的 bin 目录下:

jps (JVM Process Status): 类似 UNIX 的 ps 命令。用户查看所有 Java 进程的启动类、传入参数和 Java 虚拟机参数等信息;jstat( JVM Statistics Monitoring Tool): 用于收集 HotSpot 虚拟机各方面的运行数据;jinfo (Configuration Info for Java) : Configuration Info forJava,显示虚拟机配置信息;jmap (Memory Map for Java) :生成堆转储快照;jhat (JVM Heap Dump Browser ) : 用于分析 heapdump 文件,它会建立一个 HTTP/HTML 服务器,让用户可以在浏览器上查看分析结果;jstack (Stack Trace for Java):生成虚拟机当前时刻的线程快照,线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合。1.1 jps:查看所有 Java 进程

jps(JVM Process Status) 命令类似 UNIX 的 ps 命令。

jps:显示虚拟机执行主类名称以及这些进程的本地虚拟机唯一 ID(Local Virtual Machine Identifier,LVMID)。jps -q :只输出进程的本地虚拟机唯一 ID。

代码语言:txt复制C:\Users\SnailClimb>jps 7360 NettyClient2 17396 7972 Launcher 16504 Jps 17340 NettyServer

jps -l:输出主类的全名,如果进程执行的是 Jar 包,输出 Jar 路径。

代码语言:txt复制C:\Users\SnailClimb>jps -l 7360 firstNettyDemo.NettyClient2 17396 7972 org.jetbrains.jps.cmdline.Launcher 16492 sun.tools.jps.Jps 17340 firstNettyDemo.NettyServer

jps -v:输出虚拟机进程启动时 JVM 参数。

jps -m:输出传递给 Java 进程 main() 函数的参数。

1.2 jstat: 监视虚拟机各种运行状态信息

jstat(JVM Statistics Monitoring Tool) 使用于监视虚拟机各种运行状态信息的命令行工具。 它可以显示本地或者远程(需要远程主机提供 RMI 支持)虚拟机进程中的类信息、内存、垃圾收集、JIT 编译等运行数据,在没有 GUI,只提供了纯文本控制台环境的服务器上,它将是运行期间定位虚拟机性能问题的首选工具。

jstat 命令使用格式:

代码语言:txt复制jstat - [-t] [-h] [ []]

比如 jstat -gc -h3 31736 1000 10表示分析进程 id 为 31736 的 gc 情况,每隔 1000ms 打印一次记录,打印 10 次停止,每 3 行后打印指标头部。

常见的 option 如下:

jstat -class vmid :显示 ClassLoader 的相关信息;jstat -compiler vmid :显示 JIT 编译的相关信息;jstat -gc vmid :显示与 GC 相关的堆信息;jstat -gccapacity vmid :显示各个代的容量及使用情况;jstat -gcnew vmid :显示新生代信息;jstat -gcnewcapcacity vmid :显示新生代大小与使用情况;jstat -gcold vmid :显示老年代和永久代的信息;jstat -gcoldcapacity vmid :显示老年代的大小;jstat -gcpermcapacity vmid :显示永久代大小;jstat -gcutil vmid :显示垃圾收集信息;

另外,加上 -t参数可以在输出信息上加一个 Timestamp 列,显示程序的运行时间。

1.3 jinfo: 实时地查看和调整虚拟机各项参数

jinfo vmid :输出当前 jvm 进程的全部参数和系统属性 (第一部分是系统的属性,第二部分是 JVM 的参数)。

jinfo -flag name vmid :输出对应名称的参数的具体值。比如输出 MaxHeapSize、查看当前 jvm 进程是否开启打印 GC 日志 ( -XX:PrintGCDetails :详细 GC 日志模式,这两个都是默认关闭的)。

代码语言:txt复制C:\Users\SnailClimb>jinfo -flag MaxHeapSize 17340 -XX:MaxHeapSize=2124414976 C:\Users\SnailClimb>jinfo -flag PrintGC 17340 -XX:-PrintGC

使用 jinfo 可以在不重启虚拟机的情况下,可以动态的修改 jvm 的参数。尤其在线上的环境特别有用,请看下面的例子:

jinfo -flag [+|-]name vmid 开启或者关闭对应名称的参数。

代码语言:txt复制C:\Users\SnailClimb>jinfo -flag PrintGC 17340 -XX:-PrintGC C:\Users\SnailClimb>jinfo -flag +PrintGC 17340 C:\Users\SnailClimb>jinfo -flag PrintGC 17340 -XX:+PrintGC1.4 jmap:生成堆转储快照

jmap(Memory Map for Java)命令用于生成堆转储快照。 如果不使用 jmap 命令,要想获取 Java 堆转储,可以使用 “-XX:+HeapDumpOnOutOfMemoryError” 参数,可以让虚拟机在 OOM 异常出现之后自动生成 dump 文件,Linux 命令下可以通过 kill -3 发送进程退出信号也能拿到 dump 文件。

jmap 的作用并不仅仅是为了获取 dump 文件,它还可以查询 finalizer 执行队列、Java 堆和永久代的详细信息,如空间使用率、当前使用的是哪种收集器等。和jinfo一样,jmap有不少功能在 Windows 平台下也是受限制的。

示例:将指定应用程序的堆快照输出到桌面。后面,可以通过 jhat、Visual VM 等工具分析该堆文件。

代码语言:txt复制C:\Users\SnailClimb>jmap -dump:format=b,file=C:\Users\SnailClimb\Desktop\heap.hprof 17340 Dumping heap to C:\Users\SnailClimb\Desktop\heap.hprof ... Heap dump file created1.5 jhat: 分析 heapdump 文件

jhat 用于分析 heapdump 文件,它会建立一个 HTTP/HTML 服务器,让用户可以在浏览器上查看分析结果。

代码语言:txt复制C:\Users\SnailClimb>jhat C:\Users\SnailClimb\Desktop\heap.hprof Reading from C:\Users\SnailClimb\Desktop\heap.hprof... Dump file created Sat May 04 12:30:31 CST 2019 Snapshot read, resolving... Resolving 131419 objects... Chasing references, expect 26 dots.......................... Eliminating duplicate references.......................... Snapshot resolved. Started HTTP server on port 7000 Server is ready.

访问 http://localhost:7000/

1.6 jstack :生成虚拟机当前时刻的线程快照

jstack(Stack Trace for Java)命令用于生成虚拟机当前时刻的线程快照。线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合.

生成线程快照的目的主要是定位线程长时间出现停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等都是导致线程长时间停顿的原因。线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做些什么事情,或者在等待些什么资源。

一般使用查找耗时时间长的线程方法如下

使用jps查找出java进程的pid,如7777 或 ps -ef | grep java

使用top -p 7777观察进程情况,然后Shift+h,显示该进程的所有线程。 找出CPU消耗较多的线程id,如7788,将7788转换为16进制0x1e6c,注意是小写。 使用jstack 7777 | grep -A 10 0x1e6c 来查询出具体的线程状态。 -A 10表示查找到所在行的后10行

下面是一个线程死锁的代码。我们下面会通过 jstack 命令进行死锁检查,输出死锁信息,找到发生死锁的线程。

代码语言:txt复制public class DeadLockDemo { private static Object resource1 = new Object();//资源 1 private static Object resource2 = new Object();//资源 2 public static void main(String[] args) { new Thread(() -> { synchronized (resource1) { System.out.println(Thread.currentThread() + "get resource1"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread() + "waiting get resource2"); synchronized (resource2) { System.out.println(Thread.currentThread() + "get resource2"); } } }, "线程 1").start(); new Thread(() -> { synchronized (resource2) { System.out.println(Thread.currentThread() + "get resource2"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread() + "waiting get resource1"); synchronized (resource1) { System.out.println(Thread.currentThread() + "get resource1"); } } }, "线程 2").start(); } }

Output

代码语言:txt复制Thread[线程 1,5,main]get resource1 Thread[线程 2,5,main]get resource2 Thread[线程 1,5,main]waiting get resource2 Thread[线程 2,5,main]waiting get resource1

线程 A 通过 synchronized (resource1) 获得 resource1 的监视器锁,然后通过Thread.sleep(1000);让线程 A 休眠 1s 为的是让线程 B 得到执行然后获取到 resource2 的监视器锁。线程 A 和线程 B 休眠结束了都开始企图请求获取对方的资源,然后这两个线程就会陷入互相等待的状态,这也就产生了死锁。

通过 jstack 命令分析:

代码语言:txt复制C:\Users\SnailClimb>jps 13792 KotlinCompileDaemon 7360 NettyClient2 17396 7972 Launcher 8932 Launcher 9256 DeadLockDemo 10764 Jps 17340 NettyServer C:\Users\SnailClimb>jstack 9256

输出的部分内容如下:

代码语言:txt复制Found one Java-level deadlock: ============================= "线程 2": waiting to lock monitor 0x000000000333e668 (object 0x00000000d5efe1c0, a java.lang.Object), which is held by "线程 1" "线程 1": waiting to lock monitor 0x000000000333be88 (object 0x00000000d5efe1d0, a java.lang.Object), which is held by "线程 2" Java stack information for the threads listed above: =================================================== "线程 2": at DeadLockDemo.lambda$main$1(DeadLockDemo.java:31) - waiting to lock (a java.lang.Object) - locked (a java.lang.Object) at DeadLockDemo$$Lambda$2/1078694789.run(Unknown Source) at java.lang.Thread.run(Thread.java:748) "线程 1": at DeadLockDemo.lambda$main$0(DeadLockDemo.java:16) - waiting to lock (a java.lang.Object) - locked (a java.lang.Object) at DeadLockDemo$$Lambda$1/1324119927.run(Unknown Source) at java.lang.Thread.run(Thread.java:748) Found 1 deadlock.

可以看到 jstack 命令已经帮我们找到发生死锁的线程的具体信息。

2. Arthas 使用简介

当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决:

这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!是否有一个全局视角来查看系统的运行状况?有什么办法可以监控到JVM的实时运行状态?

使用

代码语言:txt复制wget https://alibaba.github.io/arthas/arthas-boot.jar代码语言:txt复制java -jar arthas-boot.jar

常见指令

dashboard代码语言:txt复制实时查看应用监控数据thread代码语言:txt复制查看应用程序中所有线程情况代码语言:txt复制thread threadId命令查看指定线程状态信息代码语言:txt复制thread -n {n} 打印最忙的n个线程的信息代码语言:txt复制tracetrace 类 方法代码语言:txt复制查看每个步骤耗时stack代码语言:txt复制stack 类 方法代码语言:txt复制输出当前方法被调用的调用路径watch代码语言:txt复制方便的观察到指定方法的调用情况。能观察到的范围为:返回值、抛出异常、入参,通过编写 OGNL 表达式进行对应变量的查看。RefArthas 官方文档 https://alibaba.github.io/arthas/


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3