一、JDK的命令行工具
jps
:虚拟机进程状况工具。 用户查看所有 Java 进程的启动类、传入参数和 Java 虚拟机参数等信息;jstat:
虚拟机统计信息监视工具。
用于收集 HotSpot 虚拟机各方面的运行数据;jinfo
:Java配置信息工具。用于
显示虚拟机配置信息;jmap
:Java内存映像工具。用于生成堆转储快照;jhat
: 虚拟机堆转储快照分析工具。用于分析 heapdump 文件,它会建立一个 HTTP/HTML 服务器,让用户可以在浏览器上查看分析结果;jstack
:Java堆栈跟踪工具。用于生成虚拟机当前时刻的线程快照,线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合。
1.1、jps:查看所有Java进程
类似UNIX的PS命令。
功能:显示虚拟机执行主类名称 以及 这些进程的本地虚拟机唯一ID
(1)jps -q:只输出进程的本地虚拟机唯一ID
C:\Users\SnailClimb>jps
7360 NettyClient2
17396
7972 Launcher
16504 Jps
17340 NettyServer
(2)jsp -l:输出主类全名,如果进程执行的是jar包,输出jar路径
C:\Users\SnailClimb>jps -l
7360 firstNettyDemo.NettyClient2
17396
7972 org.jetbrains.jps.cmdline.Launcher
16492 sun.tools.jps.Jps
17340 firstNettyDemo.NettyServer
(3)jps -v
:输出虚拟机进程启动时 JVM 参数
(4)jps -m
:输出传递给 Java 进程 main() 函数的参数
1.2、jstat:监视虚拟机各种运行状态信息
功能:用于监视虚拟机各种运行状态信息。可以显示本地或者远程虚拟机进程中的类信息、内存、垃圾收集器、JIT编译等运行数据。
命令格式:
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
比如 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:实时地查看和调整虚拟机各项参数
(1)jinfo vmid:输出当前JVM进程的全部参数和系统属性
(2)jinfo -flag name vmid
:输出对应名称的参数的具体值。比如输出MaxHeapSize(最大堆大小)、查看当前JVM进程是否开启打印GC日志( -XX:PrintGCDetails
:详细 GC 日志模式)。这两个都是默认关闭的:
C:\Users\SnailClimb>jinfo -flag MaxHeapSize 17340
-XX:MaxHeapSize=2124414976
C:\Users\SnailClimb>jinfo -flag PrintGC 17340
-XX:-PrintGC
(3)使用jinfo可以在不重启虚拟机情况下,动态修改JVM参数。尤其在线上环境特别有用:
jinfo -flag [+|-]name vmid
:开启或者关闭对应名称的参数
C:\Users\SnailClimb>jinfo -flag PrintGC 17340
-XX:-PrintGC
C:\Users\SnailClimb>jinfo -flag +PrintGC 17340
C:\Users\SnailClimb>jinfo -flag PrintGC 17340
-XX:+PrintGC
1.4、生成堆转储快照
如果不使用jmap命令也可以使用“-XX:+HeapDumpOnOutOfMemoryError”参数获得堆转储。
作用:可以让虚拟机在OOM异常出现之后自动生成dump文件(Linux下用kill -3发出进程退出信号也能拿到dump文件);还可以查询finalizer执行队列、Java堆和永久代详细信息(空间使用率、当前使用的是哪种收集器)
示例:将指定应用程序的堆快照输出到桌面,然后可以通过 jhat、Visual VM 等工具分析该堆文件
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 created
1.5、jhat
: 分析 heapdump 文件
分析1.4的那个dump文件,它会建立一个HTTP/HTML 服务器,让用户可以在浏览器上查看分析结果
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.
1.6、jstack
:生成虚拟机当前时刻的线程快照
线程快照:当前虚拟机内每一条线程正在执行的方法堆栈的集合。
生成线程快照目的:为了定位线程长时间出现停顿的原因,如线程死锁、死循环、请求外部资源导致的长时间等待。
jstack
作用:线程出现停顿的时候通过jstack来查看各线程的调用堆栈,就可以知道没有响应的线程到底在后台做些什么事情,或者在等待什么资源。
示例:一个线程死锁的代码,然后通过jstack
命令进行死锁检查,输出死锁信息,找到发生死锁的线程
代码:
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();
}
}
输出:
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
命令分析:
C:\Users\SnailClimb>jps 13792 KotlinCompileDaemon 7360 NettyClient2 17396 7972 Launcher 8932 Launcher 9256 DeadLockDemo 10764 Jps 17340 NettyServer C:\Users\SnailClimb>jstack 9256
输出的部分内容如下,可以看到 jstack
命令已经帮找到发生死锁的线程的具体信息:
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 <0x00000000d5efe1c0> (a java.lang.Object) - locked <0x00000000d5efe1d0> (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 <0x00000000d5efe1d0> (a java.lang.Object) - locked <0x00000000d5efe1c0> (a java.lang.Object) at DeadLockDemo$$Lambda$1/1324119927.run(Unknown Source) at java.lang.Thread.run(Thread.java:748) Found 1 deadlock.
二、JDK的可视化工具
2.1、JConsole:Java监视与管理控制台
工具简介:JConsole 是基于JMX的可视化检测、管理工具。可以很方便地检测本地以及远程服务器的Java进程的内存使用情况。可以在控制台输出console命令启动或者在JDK目录下的bin目录找到jconsole.exe然后双击启动。
(1)连接JConsole:
如果需要使用 JConsole 连接远程进程,可以在远程 Java 程序启动时加上下面这些参数:
-Djava.rmi.server.hostname=外网访问 ip 地址
-Dcom.sun.management.jmxremote.port=60001 //监控的端口号
-Dcom.sun.management.jmxremote.authenticate=false //关闭认证
-Dcom.sun.management.jmxremote.ssl=false
在使用 JConsole 连接时,远程进程地址如下:
外网访问 ip 地址:60001
(2)查看 Java 程序概况:
(3)内存监控
JConsole可以显示当前内存的详细信息。包括堆内存/非堆内存的完整信息,还可以细化到eden区、survivor区的使用情况。
如图,点击右上角的“执行 GC(G)”按钮可以强制应用程序执行一个 Full GC。
- 新生代 GC(Minor GC):指发生新生代的的垃圾收集动作,Minor GC 非常频繁,回收速度一般也比较快。
- 老年代 GC(Major GC/Full GC):指发生在老年代的 GC,出现了 Major GC 经常会伴随至少一次的 Minor GC(并非绝对),Major GC 的速度一般会比 Minor GC 的慢 10 倍以上
(4)线程监管
前面讲的 jstack
命令,不过这个是可视化的。
如图,点击最下面有一个"检测死锁 (D)"按钮,点击这个按钮可以自动为你找到发生死锁的线程以及它们的详细信息 。
2.2、Visual VM:多合一故障处理工具(强大)
VisualVM简介:提供图形界面,在Java虚拟机上运行的Java应用程序的详细信息。是最强大的运行监视和故障处理程序。
VisualVM优点:免费、不需要被监视的程序基于特殊Agent运行,对其他程序性能影响小,可直接应用在生产环境中
VisualVM作用:运行监视和故障处理、性能分析( JProfiler、YourKit收费的,它免费):
- 显示虚拟机进程、进程的配置、环境信息(jps、jinfo);
- 监视应用程序的CPU、GC、堆、方法区、线程的信息(jstat、jstack);
- dump、分析堆转储快照(jmap、jhat);
- 方法级的程序运行性能分析、可找到被调用最多和运行时间最长的方法;
- 离线程序快照:收集程序的运行时配置、线程dump、内存dump等信息建立一个快照,可将快照发送开发者处进行Bug反馈。
三、Tomcat配置调优测试
3.1、Jmeter压力测试工具(真正压力测试是在Linux测试的)
JMeter简介:是一款开源性能测试工具,类似LoadRunner ,它也提供了一个利用本地Proxy Server(代理服务器)来录制生成测试脚本的功能(不常用)、使用Badboy录制生成 JMeter脚本(常用)。
Badboy简介:Badboy是一款不错的Web自动化测试工具,Badboy提供了将Web测试脚本直接导出生成JMeter脚本的功能。
Badboy使用步骤:
- 1. 通过Badboy的官方网站下载Badboy的最新版本;
- 2. 安装Badboy。安装过程同一般的Windows 应用程序没有什么区别,安装完成后你可以在桌面和Windows开始菜单中看到相应的快捷方式——如果找不到,可以找一下Badboy安装目录下的Badboy.exe 文件,直接双击启动Badboy;
- 3. 启动Badboy,以看到下面的界面:
- 在地址栏(图中红色方框标注的部分)中输入你需要录制的Web应用的URL——这里我们以http://www.yahoo.com 为例,并点击GO 按钮开始录制。
- 4. 开始录制后,你可以直接在Badboy内嵌的浏览器(主界面的右侧)中对被测应用进行操作,所有的操作都会被记录在主界面左侧的编辑窗口中——在这个试验中,我们在Yahoo的搜索引擎中输入 JMeter 进行搜索。不过你将看到,录制下来的脚本并不是一行行的代码,而是一个个Web对象——这就有点像LoadRunner的VuGen中的Tree View视图;
- 5. 录制完成后,点击工具栏中的“停止录制”按钮,完成脚本的录制;
- 6. 选择“File -> Export to JMeter”菜单,填写文件名“login_mantis.jmx”,将录制好脚本导出为JMeter脚本格式。也可以选择“File -> Save”菜单保存为Badboy脚本;
- 7. 启动JMeter并打开刚刚生成的测试脚本。
3.2、什么是吞吐量 / JVM简单调参调优
就是“每秒查询率”,是一台服务器每秒能够响应的查询次数,是对一个特定查询服务规定时间内所处理流量的多少的衡量标准。
(1)测试串行吞吐量:
初始化测试:
-XX:+PrintGCDetails -Xmx32M -Xms1M -XX:+HeapDumpOnOutOfMemoryError -XX:+UseSerialGC -XX:PermSize=32M |
GC 回收次数25次 吞吐量4662 |
加大初始堆内存大小-Xms1M 修改为32m GC 回收次数7次 吞吐量5144 结论: 堆的 初始值和堆的最大值 一致(可提高吞吐量) |
扩大堆内存:
-XX:+PrintGCDetails -Xmx512M -Xms32M -XX:+HeapDumpOnOutOfMemoryError -XX:+UseSerialGC -XX:PermSize=32M |
堆内存扩大为512M |
GC 回收次数6次(差不多不变) 吞吐量5141(差不多不变) 结论: 垃圾回收次数 和设置最大堆内存大小无关,只和 堆初始值 有关系。 初始内存会影响吞吐量。
|
调整初始堆:
-XX:+PrintGCDetails -Xmx512M –Xms512M -XX:+HeapDumpOnOutOfMemoryError -XX:+UseSerialGC -XX:PermSize=32M |
GC回收次数0次(没有GC回收,因为初始堆值够大) 吞吐量6561次 结论: 堆的初始值和最大堆内存一致,并且初始堆越大就会高 |
(2)并行回收(UseParNewGC):
例子还是串行回收的例子
-XX:+PrintGCDetails -Xmx512M -Xms512M -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParNewGC -XX:PermSize=32M |
GC回收0次 吞吐量6800(吞吐量高了) |
CMS收集器:
-XX:+PrintGCDetails -Xmx512M -Xms512M -XX:+HeapDumpOnOutOfMemoryError -XX:+UseConcMarkSweepGC -XX:PermSize=32M |
|
G1回收方式:
-XX:+PrintGCDetails -Xmx512M -Xms512M -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -XX:PermSize=32M |
|
3.3、JVM调优总结
(1)减少GC对老年代的回收,减少GC回收次数;
(2)堆初始值和最大值一致
(3)初始堆值和最大堆内存越大,吞吐量越高
(4)最好使用并行收集器,因为并行收集器比串行收集器吞吐量高、速度快(但效率不一定比串行高)
(5)设置堆内存新生代的比例和老年代的比例最好是 1:2 或者 1:3
四、垃圾回收策略
4.1、Minor GC和Full GC区别
新生代 GC(Minor GC):
指发生在新生代的垃圾回收,以为Java对象大多具有死亡快的特性,所以新生代回收很频繁,回收速度也快;
老年代 GC(Major GC / Full GC):
指发生在老年代的 GC,出现了Major GC一般会伴随至少一次Minor GC(why?),MajorGC 的速度一般会比 Minor GC 慢 10倍以上;
Minor GC触发机制:当新生代满时就会触发Minor GC,这里的新生代满指的是Eden区代满,Survivor区满不会引发GC。
Full GC触发机制:当老年代满时会触发Full GC,Full GC会同时回收新生代、老年代(一锅端),JDK1.7以前当永久代满时也会触发Full GC。
4.2、JVM的永久代中会发生垃圾回收么?
垃圾回收不会发生在永久代,如果永久代满了或者超过了临界值会触发完全垃圾回收Full GC(这TM为啥不算触发垃圾回收?)
### 若对你有帮助的话,欢迎点赞!评论!转发!谢谢!
上一篇:垃圾收集器与内存分配策略
下一篇:调优案例分析与实战