Java虚拟机性能调优工具的安装和基础使用
1、Visual GC
虚拟机自带的Visual GC
jvisualvm是JDK提供给我们的一个功能强大的jvm(java虚拟机)监控客户端,默认其并不包含对垃圾回收的监控,我们可以通过其插件扩展的机制为 jvisualvm 增加 Visual GC 的功能,本篇经验就分享一下如何进行相关操作。
1、 查看并运行 jvisualvm
- 进入 JDK 的bin目录,找到 jvisualvm 程序
- 运行 jvisualvm,从界面上可以看出,并没有垃圾回收的相关监控
2、 打开 jvisualvm 插件窗口,并查看可用插件
- 点击 “工具”–“插件” 进入 “插件” 窗口
- 在 “插件” 窗口,点击 “可用插件” 可以看到所有可用插件
3、 选择并安装 Visual GC 插件
- 在“可用插件”列表中勾选 “Visual GC”, 并点击下方的 “安装” 按钮,经过几步确认后,等待下载安装完成。
4、 退出 jvisualvm 后再次进入
- 重启 jvisualvm 后,界面上已经展示出了 Visual GC 的页签,即垃圾回收的相关监控。
可用插件没有Visual GC
下载地址:https://visualvm.github.io/index.html
在插件中更改设置
重启
2、Arthas的安装和入门使用
Arthas 官方文档十分详细:https://alibaba.github.io/arthas
当你遇到以下类似问题而束手无策时,Arthas
可以帮助你解决:
- 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
- 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
- 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
- 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
- 是否有一个全局视角来查看系统的运行状况?
- 有什么办法可以监控到JVM的实时运行状态?
- 怎么快速定位应用的热点,生成火焰图?
- 怎样直接从JVM内查找某个类的实例?
Arthas
支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 Tab
自动补全功能,进一步方便进行问题的定位和诊断。
安装
Arthas 支持在 Linux/Unix/Mac 等平台上一键安装,请复制以下内容,并粘贴到命令行中,敲 回车
执行即可:
curl -L https://arthas.aliyun.com/install.sh | sh
curl -O https://arthas.aliyun.com/arthas-boot.jar
上述命令会下载启动脚本文件 as.sh
到当前目录,你可以放在任何地方或将其加入到 $PATH
中。
直接在shell下面执行./as.sh
,就会进入交互界面。
也可以执行./as.sh -h
来获取更多参数信息。
快速入手
参考来源:https://arthas.aliyun.com/doc/quick-start.html
1、运行一个简单的程序
curl -O https://arthas.aliyun.com/math-game.jar
java -jar math-game.jar
math-game
是一个简单的程序,每隔一秒生成一个随机数,再执行质因数分解,并打印出分解结果。
源码:
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
public class MathGame {
private static Random random = new Random();
private int illegalArgumentCount = 0;
public List<Integer> primeFactors(int number) {
if (number < 2) {
++this.illegalArgumentCount;
throw new IllegalArgumentException("number is: " + number + ", need >= 2");
}
ArrayList<Integer> result = new ArrayList<Integer>();
int i = 2;
while (i <= number) {
if (number % i == 0) {
result.add(i);
number /= i;
i = 2;
continue;
}
++i;
}
return result;
}
public static void main(String[] args) throws InterruptedException {
MathGame game = new MathGame();
while (true) {
game.run();
TimeUnit.SECONDS.sleep(1L);
}
}
public void run() throws InterruptedException {
try {
int number = random.nextInt() / 10000;
List<Integer> primeFactors = this.primeFactors(number);
MathGame.print(number, primeFactors);
}
catch (Exception e) {
System.out.println(String.format("illegalArgumentCount:%3d,", this.illegalArgumentCount) + e.getMessage());
}
}
public static void print(int number, List<Integer> primeFactors) {
StringBuffer sb = new StringBuffer(number + "=");
for (int factor : primeFactors) {
sb.append(factor).append('*');
}
if (sb.charAt(sb.length() - 1) == '*') {
sb.deleteCharAt(sb.length() - 1);
}
System.out.println(sb);
}
}
2、启动archas
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar
选择应用java进程:
3、查看dashboard
[arthas@1650]$ dashboard
ID NAME GROUP PRIORIT STATE %CPU DELTA_T TIME INTERRUP DAEMON
-1 C2 CompilerThread0 - -1 - 0.0 0.000 0:0.425 false true
-1 C1 CompilerThread1 - -1 - 0.0 0.000 0:0.267 false true
1 main main 5 TIMED_W 0.0 0.000 0:0.096 false false
-1 VM Periodic Task Thread - -1 - 0.0 0.000 0:0.084 false true
-1 VM Thread - -1 - 0.0 0.000 0:0.058 false true
22 arthas-NettyHttpTelnetBo system 5 RUNNABL 0.0 0.000 0:0.048 false true
15 arthas-NettyHttpTelnetBo system 5 RUNNABL 0.0 0.000 0:0.015 false true
8 Attach Listener system 9 RUNNABL 0.0 0.000 0:0.010 false true
16 arthas-NettyWebsocketTty system 5 RUNNABL 0.0 0.000 0:0.002 false true
23 arthas-command-execute system 5 TIMED_W 0.0 0.000 0:0.002 false true
Memory used total max usage GC
heap 16M 29M 241M 6.83% gc.copy.count 16
eden_space 621K 8384K 68288K 0.91% gc.copy.time(ms) 33
survivor_space 1023K 1024K 8512K 12.03% 1
tenured_gen 14M 20M 166M 8.96% gc.marksweepcompact.time 11
nonheap 26M 27M -1 97.30% (ms)
code_cache 4M 4M 240M 1.70%
metaspace 20M 20M -1 97.21%
Runtime
os.name Linux
os.version 3.10.0-693.el7.x86_64
java.version 1.8.0_301
ID NAME GROUP PRIORIT STATE %CPU DELTA_T TIME INTERRUP DAEMON
24 Timer-for-arthas-dashboa system 5 RUNNABL 0.4 0.020 0:0.021 false true
-1 C1 CompilerThread1 - -1 - 0.23 0.011 0:0.279 false true
22 arthas-NettyHttpTelnetBo system 5 RUNNABL 0.05 0.002 0:0.051 false true
-1 VM Periodic Task Thread - -1 - 0.04 0.002 0:0.087 false true
1 main main 5 TIMED_W 0.02 0.001 0:0.097 false false
-1 C2 CompilerThread0 - -1 - 0.01 0.000 0:0.425 false true
-1 VM Thread - -1 - 0.0 0.000 0:0.058 false true
2 Reference Handler system 10 WAITING 0.0 0.000 0:0.001 false true
3 Finalizer system 8 WAITING 0.0 0.000 0:0.001 false true
4 Signal Dispatcher system 9 RUNNABL 0.0 0.000 0:0.000 false true
Memory used total max usage GC
heap 18M 29M 241M 7.47% gc.copy.count 16
eden_space 2M 8M 66M 3.18% gc.copy.time(ms) 33
survivor_space 1023K 1024K 8512K 12.03% 1
tenured_gen 14M 20M 166M 8.96% gc.marksweepcompact.time 11
nonheap 27M 28M -1 96.63% (ms)
code_cache 4M 4M 240M 1.69%
metaspace 20M 21M -1 96.59%
Runtime
os.name Linux
os.version 3.10.0-693.el7.x86_64
java.version 1.8.0_301
4、通过thread命令来获取到math-game
进程的Main Class
thread 1
会打印线程ID 1的栈,通常是main函数的线程。
5、通过jad来反编译Main Class
[arthas@1650]$ jad demo.MathGame
ClassLoader:
+-sun.misc.Launcher$AppClassLoader@70dea4e
+-sun.misc.Launcher$ExtClassLoader@1b6d3586
Location:
/usr/local/src/math-game.jar
/*
* Decompiled with CFR.
*/
package demo;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
public class MathGame {
private static Random random = new Random();
private int illegalArgumentCount = 0;
public List<Integer> primeFactors(int number) {
/*44*/ if (number < 2) {
/*45*/ ++this.illegalArgumentCount;
throw new IllegalArgumentException("number is: " + number + ", need >= 2");
}
ArrayList<Integer> result = new ArrayList<Integer>();
/*50*/ int i = 2;
/*51*/ while (i <= number) {
/*52*/ if (number % i == 0) {
/*53*/ result.add(i);
/*54*/ number /= i;
/*55*/ i = 2;
continue;
}
/*57*/ ++i;
}
/*61*/ return result;
}
public static void main(String[] args) throws InterruptedException {
MathGame game = new MathGame();
while (true) {
/*16*/ game.run();
/*17*/ TimeUnit.SECONDS.sleep(1L);
}
}
public void run() throws InterruptedException {
try {
/*23*/ int number = random.nextInt() / 10000;
/*24*/ List<Integer> primeFactors = this.primeFactors(number);
/*25*/ MathGame.print(number, primeFactors);
}
catch (Exception e) {
/*28*/ System.out.println(String.format("illegalArgumentCount:%3d, ", this.illegalArgume
ntCount) + e.getMessage()); }
}
public static void print(int number, List<Integer> primeFactors) {
StringBuffer sb = new StringBuffer(number + "=");
/*34*/ for (int factor : primeFactors) {
/*35*/ sb.append(factor).append('*');
}
/*37*/ if (sb.charAt(sb.length() - 1) == '*') {
/*38*/ sb.deleteCharAt(sb.length() - 1);
}
/*40*/ System.out.println(sb);
}
}
Affect(row-cnt:1) cost in 978 ms.
6、watch
通过watch命令来查看demo.MathGame#primeFactors
函数的返回值:
[arthas@1650]$ watch
The argument 'class-pattern' is required, description: The full qualified class name you want to wat
ch[arthas@1650]$ watch demo.MathGame primeFactors returnObj
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 138 ms, listenerId: 1
method=demo.MathGame.primeFactors location=AtExit
ts=2021-10-14 11:43:30; [cost=0.767595ms] result=@ArrayList[
@Integer[37],
@Integer[41],
@Integer[59],
]
method=demo.MathGame.primeFactors location=AtExceptionExit
ts=2021-10-14 11:43:31; [cost=0.122073ms] result=null
method=demo.MathGame.primeFactors location=AtExit
ts=2021-10-14 11:43:32; [cost=0.96881ms] result=@ArrayList[
@Integer[2],
@Integer[33037],
]
method=demo.MathGame.primeFactors location=AtExceptionExit
ts=2021-10-14 11:43:33; [cost=0.025097ms] result=null
method=demo.MathGame.primeFactors location=AtExit
ts=2021-10-14 11:43:34; [cost=1.166202ms] result=@ArrayList[
@Integer[2],
@Integer[107119],
]
7、退出arthas
如果只是退出当前的连接,可以用quit
或者exit
命令。Attach到目标进程上的arthas还会继续运行,端口会保持开放,下次连接时可以直接连接上。
如果想完全退出arthas,可以执行stop
命令。