Bootstrap

Arthas对Windows下java程序性能监控历时30H整理资料(建议收藏)

1 背景

在这里插入图片描述

当遇到 Java 线上问题时,如 CPU 飙升、负载突高、内存溢出等问题,可使用top,查命令,查网络,然后 jps、jstack、jmap、jhat、jstat、hprof ,这里主要介绍Arthas(阿尔萨斯)在windows下对于本机Java程序的监控,因为目前现有的arthas监控基本都是在Linux下进行的!

2 启动Arthas

curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar

此时系统会列出当前可监控的进程,序号2为我当前windows系统下的tomcat进程,因此输入2,enter键,进入Arthas的监控控制台。

1 启动问题

由于在windows机器下启动,在启动时程序报错了
Caused by: com.sun.tools.attach.AttachNotSupportedException: no providers installed
由于我本机系统是windows,直接安装的java,发现Windows 下安装 JDK 时会同时安装一个独立的 jre ,独立安装的 jre 下没有 attach.dll 这个文件,需要将%JAVA_HOME%/jdk/jre/bin下的 attach.dll 拷贝到%JRE_HOME%/bin下,当发现我jdk中没有attach.dll,由于我的jdk版本是1.8,我下载了一个JDK1.8的压缩包,然后在压缩包jre/bin下找到了attach.dll这个文件。然后复制到我jre/bin 和 java_home/bin目录下就启动成功了!

JDK1.8的attach.dll文件

2 退出

arthas控制台执行quit或者exit命令!!!

3 性能监控

1 dashboard面板分析(重要)

输入q可退出dashboard面板

  • ID: Java 级别的线程 ID,注意这个 ID 不能跟 jstack 中的 nativeID 一一对应。
    - NAME: 线程名
  • GROUP: 线程组名
  • PRIORITY: 线程优先级, 1~10 之间的数字,越大表示优先级越高
  • STATE: 线程的状态
  • CPU%:线程的 cpu 使用率。比如采样间隔 1000ms,某个线程的增量 cpu 时间为 100ms,则 cpu使用率=100/1000=10%
  • DELTA_TIME: 上次采样之后线程运行增量 CPU 时间,数据格式为秒 TIME: 线程运行总CPU 时间,数据格式为分:秒
  • INTERRUPTED: 线程当前的中断位状态 DAEMON: 是否是 daemon 线程
    在这里插入图片描述

2 heapdump

dump java heap, 类似 jmap 命令的 heap dump 功能。
在这里插入图片描述
执行这个命令后,文件在我tomcat/bin目录下生成了。

hprof文件分析
由于得到的文件较大,请压缩后,上传在线分析网站进行分析!
Hprof文件在线分析地址

3 JVM虚拟机分析(重要)

[arthas@1352]$ jvm
 RUNTIME
-----------------------------------------------------------------------------------------------------------------------
 MACHINE-NAME                      1352@DESKTOP-EGKGE2A
 JVM-START-TIME                    2023-11-10 13:55:34
 MANAGEMENT-SPEC-VERSION           1.2
 SPEC-NAME                         Java Virtual Machine Specification
 SPEC-VENDOR                       Oracle Corporation
 SPEC-VERSION                      1.8
 VM-NAME                           Java HotSpot(TM) 64-Bit Server VM
 VM-VENDOR                         Oracle Corporation
 VM-VERSION                        25.202-b08
 INPUT-ARGUMENTS                   -Djava.util.logging.config.file=C:\Users\Administrator\AppData\Local\JetBrains\Inte
                                   lliJIdea2023.2\tomcat\0ed26788-34cb-4ae8-98a8-5d8371dc4953\conf\logging.properties
                                   -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
                                   -Dcom.sun.management.jmxremote=
                                   -Dcom.sun.management.jmxremote.port=1099
                                   -Dcom.sun.management.jmxremote.ssl=false
                                   -Dcom.sun.management.jmxremote.password.file=C:\Users\Administrator\AppData\Local\J
                                   etBrains\IntelliJIdea2023.2\tomcat\0ed26788-34cb-4ae8-98a8-5d8371dc4953\jmxremote.p
                                   assword
                                   -Dcom.sun.management.jmxremote.access.file=C:\Users\Administrator\AppData\Local\Jet
                                   Brains\IntelliJIdea2023.2\tomcat\0ed26788-34cb-4ae8-98a8-5d8371dc4953\jmxremote.acc
                                   ess
                                   -Djava.rmi.server.hostname=127.0.0.1
                                   -Djdk.tls.ephemeralDHKeySize=2048
                                   -Djava.protocol.handler.pkgs=org.apache.catalina.webresources
                                   -Dignore.endorsed.dirs=
                                   -Dcatalina.base=C:\Users\Administrator\AppData\Local\JetBrains\IntelliJIdea2023.2\t
                                   omcat\0ed26788-34cb-4ae8-98a8-5d8371dc4953
                                   -Dcatalina.home=F:\apache-tomcat-8.5.35
                                   -Djava.io.tmpdir=F:\apache-tomcat-8.5.35\temp
 CLASS-PATH                        F:\apache-tomcat-8.5.35\bin\bootstrap.jar;F:\apache-tomcat-8.5.35\bin\tomcat-juli.j
                                   ar
 BOOT-CLASS-PATH                   D:\jdk1.8.0_202_x64\jre\lib
                                   esources.jar;D:\jdk1.8.0_202_x64\jre\lib
                                   t.jar;D:\jdk1.8.0_202_x64\jre\lib\sunrsasign.jar;D:\jdk1.8.0_202_x64\jre\lib\jsse.j
                                   ar;D:\jdk1.8.0_202_x64\jre\lib\jce.jar;D:\jdk1.8.0_202_x64\jre\lib\charsets.jar;D:\
                                   jdk1.8.0_202_x64\jre\lib\jfr.jar;D:\jdk1.8.0_202_x64\jre\classes
 LIBRARY-PATH                      D:\jdk1.8.0_202_x64\jre\bin;C:\Windows\Sun\Java\bin;C:\Windows\system32;C:\Windows;
                                   C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\Windows
                                   PowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Program Files\Git\cmd;C:\Program F
                                   iles\Bandizip\;E:\nodejs\;E:\寰俊web寮?鍙戣?呭伐鍏穃dll;C:\Users\Administrator\AppData\Local\Mi
                                   crosoft\WindowsApps;D:\jdk1.8.0_202_x64\bin;D:\jdk1.8.0_202_x64\jre\bin;D:\maven\bi
                                   n;C:\Users\Administrator\AppData\Roaming\npm;.

-----------------------------------------------------------------------------------------------------------------------
 CLASS-LOADING
-----------------------------------------------------------------------------------------------------------------------
 LOADED-CLASS-COUNT                12676
 TOTAL-LOADED-CLASS-COUNT          12679
 UNLOADED-CLASS-COUNT              3
 IS-VERBOSE                        false

-----------------------------------------------------------------------------------------------------------------------
 COMPILATION
-----------------------------------------------------------------------------------------------------------------------
 NAME                              HotSpot 64-Bit Tiered Compilers
 TOTAL-COMPILE-TIME                52556
 [time (ms)]

-----------------------------------------------------------------------------------------------------------------------
 GARBAGE-COLLECTORS
-----------------------------------------------------------------------------------------------------------------------
 PS Scavenge                       name : PS Scavenge
 [count/time (ms)]                 collectionCount : 15
                                   collectionTime : 293
 PS MarkSweep                      name : PS MarkSweep
 [count/time (ms)]                 collectionCount : 3
                                   collectionTime : 409

-----------------------------------------------------------------------------------------------------------------------
 MEMORY-MANAGERS
-----------------------------------------------------------------------------------------------------------------------
 CodeCacheManager                  Code Cache
 Metaspace Manager                 Metaspace
                                   Compressed Class Space
 PS Scavenge                       PS Eden Space
                                   PS Survivor Space
 PS MarkSweep                      PS Eden Space
                                   PS Survivor Space
                                   PS Old Gen

-----------------------------------------------------------------------------------------------------------------------
 MEMORY
-----------------------------------------------------------------------------------------------------------------------
 HEAP-MEMORY-USAGE                 init : 266338304(254.0 MiB)
 [memory in bytes]                 used : 1331906816(1.2 GiB)
                                   committed : 1591738368(1.5 GiB)
                                   max : 3787980800(3.5 GiB)
 NO-HEAP-MEMORY-USAGE              init : 2555904(2.4 MiB)
 [memory in bytes]                 used : 126880912(121.0 MiB)
                                   committed : 131399680(125.3 MiB)
                                   max : -1(-1 B)
 PENDING-FINALIZE-COUNT            0

-----------------------------------------------------------------------------------------------------------------------
 OPERATING-SYSTEM
-----------------------------------------------------------------------------------------------------------------------
 OS                                Windows 10
 ARCH                              amd64
 PROCESSORS-COUNT                  4
 LOAD-AVERAGE                      -1.0
 VERSION                           10.0

-----------------------------------------------------------------------------------------------------------------------
 THREAD
-----------------------------------------------------------------------------------------------------------------------
 COUNT                             65
 DAEMON-COUNT                      53
 PEAK-COUNT                        66
 STARTED-COUNT                     85
 DEADLOCK-COUNT                    0

-----------------------------------------------------------------------------------------------------------------------
 FILE-DESCRIPTOR
-----------------------------------------------------------------------------------------------------------------------
 MAX-FILE-DESCRIPTOR-COUNT         -1
 OPEN-FILE-DESCRIPTOR-COUNT        -1

Thread相关

  • COUNT: JVM 当前活跃的线程数
  • DAEMON-COUNT: JVM 当前活跃的守护线程数
  • PEAK-COUNT: 从 JVM启动开始曾经活着的最大线程数
  • STARTED-COUNT: 从 JVM 启动开始总共启动过的线程次数
  • DEADLOCK-COUNT:JVM 当前死锁的线程数

4 Logger

可查看logger信息,更改logger日志级别

[arthas@1352]$ logger
 name                root
 class               org.apache.log4j.spi.RootLogger





 classLoaderHash     f60ec6f
 level               DEBUG
 effectiveLevel      DEBUG
 additivity          true
 codeSource          file:/E:/dev/zhixia_jght/mgr/target/mgr/WEB-INF/lib/log4j-1.2.17.jar
 appenders           name            console
                     class           org.apache.log4j.ConsoleAppender
                                                        Loader


                                                  arent Classloader:
                                                 ClassLoader@5e91993f
                     classLoaderHash f60ec6f
                     target          System.err

更新logger级别

[arthas@2062]$ logger --name ROOT --level debug
update logger level success.

5 memory内存分析(重要)

Memory                                             used             total            max              usage
heap                                               352M             1438M            3612M            9.76%
ps_eden_space                                      253M             1174M            1262M            20.09%
ps_survivor_space                                  34M              34M              34M              99.96%
ps_old_gen                                         64M              230M             2709M            2.38%
nonheap                                            122M             126M             -1               96.55%
code_cache                                         41M              41M              240M             17.26%
metaspace                                          72M              76M              -1               95.43%
compressed_class_space                             8M               9M               1024M            0.82%
direct                                             80K              80K              -                100.00%
mapped                                             0K               0K               -                0.00%

6 monitor监控接口调用(重要)

monitor 全限定类名 方法名(填*为监控当前类的所有方法) -c 1
-c 监控周期 每/秒刷新一次

[arthas@1352]$ monitor com.hailas.zhixiao.controller.ChouJiangController * -c 1
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 15) cost in 85 ms, listenerId: 8
 timestamp         class                       method                     total    success   fail     avg-rt(  fail-ra
                                                                                                      ms)      te
-----------------------------------------------------------------------------------------------------------------------
 2023-11-10 16:20  com.hailas.zhixiao.control  getChouJiangList           1        1         0        952.39   0.00%
 :01               ler.ChouJiangController

 timestamp         class                       method                     total    success   fail     avg-rt(  fail-ra
                                                                                                      ms)      te
-----------------------------------------------------------------------------------------------------------------------
 2023-11-10 16:20  com.hailas.zhixiao.control  getChouJiangList           0        0         0        0.00     0.00%
 :02               ler.ChouJiangController

 timestamp         class                       method                     total    success   fail     avg-rt(  fail-ra
                                                                                                      ms)      te
-----------------------------------------------------------------------------------------------------------------------
 2023-11-10 16:20  com.hailas.zhixiao.control  getChouJiangList           0        0         0        0.00     0.00%
 :03               ler.ChouJiangController

7 trace调用链路时间分析(重要)

为了让小伙伴更好的了解trace,本次写了一个统计排序的接口,主要统计选择排序,冒泡排序,插入排序,官方排序所消耗的时间,入参为要排序的数组元素的个数N,内部程序会在100万个数字中随机生成不重复的N个元素,然后执行方法进行排序。代码如下:

package com.itgz.gzgream;

import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;

@Controller
public class AlgorithmController {


    @RequestMapping("/countTime")
    public  void   getOrderMethodTime(Integer i, HttpServletResponse response) throws IOException {
        response.setContentType("text/html");
        response.setCharacterEncoding("Utf-8");
        long time = System.currentTimeMillis();
        Integer[] integers = generateRandomNumbers(i);
        long randTime=System.currentTimeMillis()-time;
        selectionSort(integers);
        long selectionSortime=System.currentTimeMillis()-randTime-time;
        bubblesort(integers);
        long bubblesortTime=System.currentTimeMillis()-randTime-time-selectionSortime;
        insertionsert(integers);
        long insertionsertTime=System.currentTimeMillis()-randTime-time-selectionSortime-bubblesortTime;
        comparator(integers);
        long comparatorTime=System.currentTimeMillis()-randTime-time-selectionSortime-bubblesortTime-insertionsertTime;
        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<head>");
        out.println("<title>简单统计几种排序的完成时间</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h1>简单统计几种排序的完成时间</h1><br>");
        out.println("<h2 style=\"color:blue\">随机生成数组时间:"+randTime+"ms<h2><br>");
        out.println("<h2 style=\"color:blue\">选择排序时间:"+selectionSortime+"ms</h2><br>");
        out.println("<h2 style=\"color:blue\">冒泡排序时间:"+bubblesortTime+"ms<h2><br>");
        out.println("<h2 style=\"color:blue\">插入排序(对数器)时间:"+insertionsertTime+"ms<h2><br>");
        out.println("<h2 style=\"color:blue\">官方给的排序时间:"+comparatorTime+"ms<h2><br>");
        out.println("</body>");
        out.println("</html>");

    }

    /**
     * 选择排序:时间复杂度O(n^2)  空间复杂度O(1)
     */
    public static void selectionSort(Integer[] arr){
        if(arr == null || arr.length < 2){
            return ;
        }
        for (int i = 0; i < arr.length; i++) {
            int minIndex = i;
            for (int j = i + 1; j < arr.length; j++) {
                minIndex = arr[j] < arr[minIndex] ? j: minIndex;//i ~ N-1 找出最小值的下标
            }
            int tmp = arr[i];
            arr[i] = arr[minIndex];
            arr[minIndex] = tmp;
        }
    }

    /**
     * 冒泡排序:时间复杂度O(n^2)  空间复杂度O(1)
     * @param arr
     */
    public static void  bubblesort(Integer[] arr){
        if (arr == null || arr.length < 2){
            return;
        }
        //0~N 0~N-1 0~N-2 ...
        for (int i = arr.length -1 ; i > 0 ; i--) { //i = arr.length -1
            for (int j = 0; j < i; j++) { // j = 0; j < i
                if(arr[j] > arr[j+1]){
                    int tmp = arr[i];
                    arr[i] = arr[j];
                    arr[j] = tmp;

                }
            }

        }

    }


    /**
     * 插入排序 时间复杂度O(n^2) 但是常数项比前两个更优  空间复杂度O(1)
     */
    public static void insertionsert(Integer[] arr){
        if (arr == null || arr.length < 2){
            return;
        }

        for (int i = 1; i < arr.length; i++) {
            for (int j = i-1 ;j >= 0 && arr[j] > arr[j+1] ; j--) {
                int tmp = arr[i];
                arr[i] = arr[j];
                arr[j] = tmp;
            }

        }
    }


    /**
     * 官方给的排序
     */
    public static void comparator(Integer[] arr){
        Arrays.sort(arr);
    }



    public static Integer[] generateRandomNumbers(int n) {
        if (n > 1000000) {
            throw new IllegalArgumentException("n must be less than or equal to max");
        }

        Set<Integer> set = new HashSet<>();
        Random random = new Random();

        while (set.size() < n) {
            int num = random.nextInt(1000000) + 1;
            set.add(num);
        }
        Integer[] ints = new Integer[set.size()];

        return set.toArray(ints);
    }
}

在这里插入图片描述
使用trace对该请求进行分析:
trace com.itgz.gzgream.AlgorithmController getOrderMethodTime -n 5 --skipJDKMethod false

  • trace:命令本身,用于启动追踪
  • com.itgz.gzgream.AlgorithmController:要追踪的类的全限定名,这里是 BasicController 类所在的包路径
  • getOrderMethodTime:要追踪的方法名,这里是 AlgorithmController类中的 getOrderMethodTime方法
  • -n 5:执行追踪的次数,这里设置为 5 次
  • –skipJDKMethod false:是否跳过 JDK 自带方法的追踪,这里设置为不跳过 JDK 方法
    执行结果
[arthas@8956]$ trace com.itgz.gzgream.AlgorithmController getOrderMethodTime  -n 5 --skipJDKMethod false
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 87 ms, listenerId: 1
`---ts=2023-11-12 18:58:48;thread_name=http-nio-8080-exec-5;id=27;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@3874b815
    `---[349.9265ms] com.itgz.gzgream.AlgorithmController:getOrderMethodTime()
        +---[0.01% 0.0257ms ] jakarta.servlet.http.HttpServletResponse:setContentType() #20
        +---[0.01% 0.0175ms ] jakarta.servlet.http.HttpServletResponse:setCharacterEncoding() #21
        +---[0.00% 0.0103ms ] java.lang.System:currentTimeMillis() #22
        +---[0.74% 2.5941ms ] com.itgz.gzgream.AlgorithmController:generateRandomNumbers() #23
        +---[0.00% 0.008ms ] java.lang.System:currentTimeMillis() #24
        +---[69.21% 242.1928ms ] com.itgz.gzgream.AlgorithmController:selectionSort() #25
        +---[0.00% 0.0075ms ] java.lang.System:currentTimeMillis() #26
        +---[28.75% 100.615ms ] com.itgz.gzgream.AlgorithmController:bubblesort() #27
        +---[0.00% 0.0074ms ] java.lang.System:currentTimeMillis() #28
        +---[0.16% 0.5573ms ] com.itgz.gzgream.AlgorithmController:insertionsert() #29
        +---[0.00% 0.0075ms ] java.lang.System:currentTimeMillis() #30
        +---[0.13% 0.4651ms ] com.itgz.gzgream.AlgorithmController:comparator() #31
        +---[0.00% 0.0063ms ] java.lang.System:currentTimeMillis() #32
        +---[0.01% 0.0261ms ] jakarta.servlet.http.HttpServletResponse:getWriter() #33
        +---[0.01% 0.0266ms ] java.io.PrintWriter:println() #34
        +---[0.01% 0.0216ms ] java.io.PrintWriter:println() #35
        +---[0.01% 0.0189ms ] java.io.PrintWriter:println() #36
        +---[0.00% 0.011ms ] java.io.PrintWriter:println() #37
        +---[0.00% 0.0123ms ] java.io.PrintWriter:println() #38
        +---[0.01% 0.0249ms ] java.io.PrintWriter:println() #39
        +---[0.04% 0.1518ms ] java.io.PrintWriter:println() #40
        +---[0.01% 0.0377ms ] java.io.PrintWriter:println() #41
        +---[0.00% 0.0145ms ] java.io.PrintWriter:println() #42
        +---[0.00% 0.0139ms ] java.io.PrintWriter:println() #43
        +---[0.00% 0.0142ms ] java.io.PrintWriter:println() #44
        +---[0.03% 0.1102ms ] java.io.PrintWriter:println() #45
        `---[0.00% 0.0115ms ] java.io.PrintWriter:println() #46

和程序中返回的结果是一致的!
在这里插入图片描述
调用耗时过滤-重要
trace com.itgz.gzgream.AlgorithmController getOrderMethodTime ‘#cost > 1’
‘#cost > 1’ 表示只显示方法执行耗时大于 1ms 的执行情况,根据耗时进行过滤,有助于在排查问题的时候,只关注异常情况。

[arthas@8956]$ trace com.itgz.gzgream.AlgorithmController getOrderMethodTime  '#cost > 1'
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 40 ms, listenerId: 2
`---ts=2023-11-12 19:08:19;thread_name=http-nio-8080-exec-8;id=2a;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@3874b815
    `---[352.6683ms] com.itgz.gzgream.AlgorithmController:getOrderMethodTime()
        +---[0.01% 0.0184ms ] jakarta.servlet.http.HttpServletResponse:setContentType() #20
        +---[0.00% 0.009ms ] jakarta.servlet.http.HttpServletResponse:setCharacterEncoding() #21
        +---[0.42% 1.4875ms ] com.itgz.gzgream.AlgorithmController:generateRandomNumbers() #23
        +---[70.06% 247.0703ms ] com.itgz.gzgream.AlgorithmController:selectionSort() #25
        +---[29.02% 102.3496ms ] com.itgz.gzgream.AlgorithmController:bubblesort() #27
        +---[0.16% 0.5745ms ] com.itgz.gzgream.AlgorithmController:insertionsert() #29
        +---[0.10% 0.356ms ] com.itgz.gzgream.AlgorithmController:comparator() #31
        `---[0.01% 0.0256ms ] jakarta.servlet.http.HttpServletResponse:getWriter() #33

trace多个类或者多个函数
trace -E 全限定类型A|全限定类名B 方法1|方法二2|方法3

8 stace调用链路分析(重要)

输出当前方法被调用的调用路径
  很多时候我们都知道一个方法被执行,但这个方法被执行的路径非常多,或者你根本就不知道这个方法是从那里被执行了,此时你需要的是 stack 命令。
查看 AlgorithmController 类中 getOrderMethodTime() 方法调用情况
stack com.itgz.gzgream.AlgorithmController getOrderMethodTime

[arthas@8956]$ stack com.itgz.gzgream.AlgorithmController getOrderMethodTime
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 24 ms, listenerId: 3
ts=2023-11-12 19:18:34;thread_name=http-nio-8080-exec-1;id=23;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@3874b815
    @com.itgz.gzgream.AlgorithmController.getOrderMethodTime()
        at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-2)
        at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:568)
        at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:884)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1081)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:974)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1011)
        at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903)
        at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564)
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885)
        at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
        at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
        at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:340)
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391)
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:896)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1744)
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:833)

根据执行时间过滤,统计AlgorithmController类中getOrderMethodTime执行超过1ms的链路调用情况
stack com.itgz.gzgream.AlgorithmController getOrderMethodTime ‘#cost > 1’

[arthas@8956]$ stack com.itgz.gzgream.AlgorithmController getOrderMethodTime  '#cost > 1'
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 20 ms, listenerId: 4
ts=2023-11-12 19:21:21;thread_name=http-nio-8080-exec-4;id=26;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@3874b815
    @com.itgz.gzgream.AlgorithmController.getOrderMethodTime()
        at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-2)
        at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:568)
        at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:884)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1081)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:974)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1011)
        at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903)
        at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564)
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885)
        at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
        at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
        at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:340)
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391)
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:896)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1744)
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:833)

9 watch生产环境接口实时分析(重要)

watch - 方法执行数据观测, 观察:返回值、抛出异常、入参,通过编写OGNL 表达式进行对应变量的查看

  • 应用场景:查看方法调用栈,参数入参,返回值等调试
  • 默认的 观察表达式,默认值是{params, target, returnObj}
  • -x 参数表示遍历深度,可以调整来打印具体的参数和结果内容,默认值是 1, 最大是4

watch com.itgz.gzgream.AlgorithmController * {params,returnObj} -x 1

[arthas@8956]$ watch com.itgz.gzgream.AlgorithmController * {params,returnObj} -x 1
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 7) cost in 21 ms, listenerId: 6
method=com.itgz.gzgream.AlgorithmController.generateRandomNumbers location=AtExit
ts=2023-11-12 19:29:37; [cost=0.042901ms] result=@ArrayList[
    @Object[][isEmpty=false;size=1],
    @Integer[][isEmpty=false;size=10],
]
method=com.itgz.gzgream.AlgorithmController.selectionSort location=AtExit
ts=2023-11-12 19:29:37; [cost=0.0101ms] result=@ArrayList[
    @Object[][isEmpty=false;size=1],
    null,
]
method=com.itgz.gzgream.AlgorithmController.bubblesort location=AtExit
ts=2023-11-12 19:29:37; [cost=0.0238ms] result=@ArrayList[
    @Object[][isEmpty=false;size=1],
    null,
]
method=com.itgz.gzgream.AlgorithmController.insertionsert location=AtExit
ts=2023-11-12 19:29:37; [cost=0.0053ms] result=@ArrayList[
    @Object[][isEmpty=false;size=1],
    null,
]
method=com.itgz.gzgream.AlgorithmController.comparator location=AtExit
ts=2023-11-12 19:29:37; [cost=0.0142ms] result=@ArrayList[
    @Object[][isEmpty=false;size=1],
    null,
]
method=com.itgz.gzgream.AlgorithmController.getOrderMethodTime location=AtExit
ts=2023-11-12 19:29:37; [cost=2.4029ms] result=@ArrayList[
    @Object[][isEmpty=false;size=2],
    null,
]

10 生产环境代码偷天换日(重要)

  • jad 把字节码文件反编译成源代码
  • mc 在内存中把源代码编译成字节码文件
  • redefine 把新生成的字节码文件在内存中执行

jad

jad --source-only net.wnn.archwebproject.ProductController > /dev1/ProductController.java

mc

mc Memory Compiler/内存编译器,编译.java文件生成.class
在内存中编译Hello.java为Hello.class
mc /root/Hello.java
可以通过-d命令指定输出目录(输出对应的路径会有package路径)
mc -d /root/test /root/Hello.java

redefine

  • 加载外部的.class文件,redefine到JVM里
  • redefine后的原来的类不能恢复,redefine有可能失败(比如增加了新的field) 不允许新增加field/method
  • 正在跑的函数,没有退出不能生效 比如main

在这里插入图片描述

;