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

jvisualvm配置java启动 jvisualvm的使用_Visual

jvisualvm配置java启动 jvisualvm的使用_jar_02

jvisualvm配置java启动 jvisualvm的使用_jar_03

在插件中更改设置

jvisualvm配置java启动 jvisualvm的使用_java_04

jvisualvm配置java启动 jvisualvm的使用_Visual_05

jvisualvm配置java启动 jvisualvm的使用_jar_06

重启

jvisualvm配置java启动 jvisualvm的使用_jvisualvm配置java启动_07

2、Arthas的安装和入门使用

Arthas 官方文档十分详细:https://alibaba.github.io/arthas

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

  1. 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
  2. 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
  3. 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
  4. 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
  5. 是否有一个全局视角来查看系统的运行状况?
  6. 有什么办法可以监控到JVM的实时运行状态?
  7. 怎么快速定位应用的热点,生成火焰图?
  8. 怎样直接从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进程:

jvisualvm配置java启动 jvisualvm的使用_jar_08

jvisualvm配置java启动 jvisualvm的使用_java_09

3、查看dashboard

jvisualvm配置java启动 jvisualvm的使用_java_10

[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函数的线程。

jvisualvm配置java启动 jvisualvm的使用_java_11

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],
]

jvisualvm配置java启动 jvisualvm的使用_java_12

7、退出arthas

如果只是退出当前的连接,可以用quit或者exit命令。Attach到目标进程上的arthas还会继续运行,端口会保持开放,下次连接时可以直接连接上。

如果想完全退出arthas,可以执行stop命令。