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