下载启动
下载地址:arthas.aliyun.com/doc/downloa…
启动:java -Dfile.encoding=UTF-8 -jar ./arthas-boot.jar然后选择要附着的进程
arthas依赖JDK,因此在服务器上需要安装JDK
输入help命令获取帮助文档
注意:从jdk21开始,java提供了一个参数-XX:+EnableDynamicAgentLoading来显示启用动态加载agent(该版本默认启用),并可能将在22及以后的版本默认禁用动态加载agent。
见JEP 451:openjdk.org/jeps/451
常用命令
jvm 查看JVM相关信息
## 查看某容器应用
[arthas@1]$ jvm
RUNTIME
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
MACHINE-NAME 1@cbms-app-sit-79565b798d-dq88b
JVM-START-TIME 2023-09-29 13:04:47
MANAGEMENT-SPEC-VERSION 1.2
SPEC-NAME Java Virtual Machine Specification
SPEC-VENDOR Oracle Corporation
SPEC-VERSION 1.8
VM-NAME OpenJDK 64-Bit Server VM
VM-VENDOR Oracle Corporation
VM-VERSION 25.222-b10
## JVM接收的参数
INPUT-ARGUMENTS -javaagent:/app/skywalking-agent/skywalking-agent.jar
-Dfile.encoding=UTF-8
-Xms3000m
-Xmx7200m
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/app/amix
## java -jar 启动
CLASS-PATH /amix-2.5.0.jar:/app/skywalking-agent/skywalking-agent.jar
BOOT-CLASS-PATH /usr/local/openjdk-8/jre/lib/resources.jar:/usr/local/openjdk-8/jre/lib/rt.jar:/usr/local/openjdk-8/jre/lib/sunrsasign.jar:/usr/local/openjdk-8/jre/li
b/jsse.jar:/usr/local/openjdk-8/jre/lib/jce.jar:/usr/local/openjdk-8/jre/lib/charsets.jar:/usr/local/openjdk-8/jre/lib/jfr.jar:/usr/local/openjdk-8/jr
e/classes
LIBRARY-PATH /usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
CLASS-LOADING
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
LOADED-CLASS-COUNT 57068
TOTAL-LOADED-CLASS-COUNT 60880
## 如果这个值很大,需要看看内存情况,是否因为内存不足导致被迫换出
UNLOADED-CLASS-COUNT 3812
IS-VERBOSE false
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
COMPILATION
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
NAME HotSpot 64-Bit Tiered Compilers
TOTAL-COMPILE-TIME 489158
[time (ms)]
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
GARBAGE-COLLECTORS
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## PS GC 年轻代
PS Scavenge name : PS Scavenge
[count/time (ms)] collectionCount : 1729
collectionTime : 40492
## PS GC 老年代
PS MarkSweep name : PS MarkSweep
[count/time (ms)] collectionCount : 25
collectionTime : 30811
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
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 : 3145728000(2.9 GiB)
[memory in bytes] used : 669297048(638.3 MiB)
committed : 4256694272(4.0 GiB)
max : 6710886400(6.3 GiB)
## 堆外内存 = Code Cache + Metaspace
## 直接内存(Direct Memory)单独计算,JVM自身会占用一部分
## 堆外内存都可以使用对应的Max*Size配置最大值,一般不配置;通常直接内存都为0,
## 在WEB后端程序中,一般很少主动使用直接内存,主流的框架默认都不使用直接内存(如nacos等)
## 因此计算最大内存需求时考虑堆内存+代码缓存+元空间+线程栈和+直接内存
## 线程栈内存默认1M,一般WEB后台程序中预留100-300M
## 通常启动时堆空间都是不满的,堆外内存溢出不会突然显现(操作系统虽然给满了虚拟内存但实际上不会一次性给那么多物理内存)
## 但是随着使用峰值而突然超出阈值导致内存不足从而换入swp导致性能下降甚至被OS主动Kill,尤其时在容器中时要注意可能会OOMKilll,如下方容器OOM示例
## 因此操作系统可用的用户空间内存一般要大于堆内存空间+堆外内存预留空间
NO-HEAP-MEMORY-USAGE init : 2555904(2.4 MiB)
[memory in bytes] used : 534662568(509.9 MiB)
committed : 605052928(577.0 MiB)
max : -1(-1 B)
## JDK1.8中都已经置为过时了,后续的JDK可能会直接删除,因此如果有用finalize最好优化掉
PENDING-FINALIZE-COUNT 0
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
OPERATING-SYSTEM
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
OS Linux
ARCH amd64
PROCESSORS-COUNT 3
LOAD-AVERAGE 0.51
VERSION 5.10.179-1.el7.x86_64
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
THREAD
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## 线程数,线程数过高也是不可忽视的,一方面栈内存开销会直接增加,另一方面活动的线程过多也会导致CPU竞争激烈
COUNT 165
DAEMON-COUNT 88
PEAK-COUNT 181
STARTED-COUNT 291044
## 死锁
DEADLOCK-COUNT 0
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
FILE-DESCRIPTOR
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## 文件描述符.OPEN数量过大也需要排查下原因
MAX-FILE-DESCRIPTOR-COUNT 1048576
OPEN-FILE-DESCRIPTOR-COUNT 469
# 某容器中因为堆外内存不够而OOM示例,设置容器内存时仅考虑了堆内存,因此启动过程中就直接导致OOMKill
# k8s文档:https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/assign-memory-resource/
# memory limit: 1010M
# jvm heap limit: 1000M
# 预留的内存过少,在峰值时就OOM了
- containerID: docker://c40f84bb3169c377b57300a976bb2f10eaa9c927fe0637ad7382450936ed8844
image: registry.xxx.com/xxx/xxx-uat2:2023.9.8-173241-release-UAT2-V3
imageID: docker-pullable://registry.xxx.com/xxx/xxx-uat2@sha256:e53adbb497b0fc7bf851edcf6d78e4707bdf349228d00e5fd5110e4389351a65
lastState:
terminated:
containerID: docker://22f800545462fad80aa6681ee4d302cb27a65b27f2d41dfe3b7347e3ff4e8aab
exitCode: 137
finishedAt: "2023-10-07T14:56:15Z"
reason: OOMKilled
startedAt: "2023-10-07T14:40:57Z"
name: cbms-fund-uat2
dashboard 实时信息面板
文档:arthas.aliyun.com/doc/dashboa…
memory 查看内存信息
[arthas@14844]$ memory
## 内存类型 使用量 总量 最大限制量 使用率(使用量/总量)
Memory used total max usage
## 堆
heap 81M 162M 3886M 2.11%
## GC相关
g1_survivor_space 4M 6M -1 72.05%
g1_eden_space 12M 66M -1 18.18%
g1_old_gen 65M 90M 3886M 1.69%
## 堆外
nonheap 113M 115M -1 97.69%
## 元空间
metaspace 80M 81M -1 99.17%
## 编译后的类空间
compressed_class_space 10M 11M 1024M 1.06%
## 代码缓存
codecache 22M 23M 48M 46.07%
## 内存映射量
mapped 0K 0K - 0.00%
## 直接内存
direct 16M 16M - 100.00%
## 内存映射量
mapped - 'non-volatile memory' 0K 0K - 0.00%
classloader 查看或使用类加载器
## 按类加载实例进行统计,主要为获取到类加载器实例的hash并在其他地方用-c参数指定该类加载器
[arthas@11288]$ classloader -l
name loadedCount hash parent
BootstrapClassLoader 5108 null null
com.taobao.arthas.agent.ArthasClassloader@2fd43f45 1384 2fd43f45 jdk.internal.loader.ClassLoader
s$PlatformClassLoader@1dc4f3f3
jdk.internal.loader.ClassLoaders$AppClassLoader@2b193f2d 10588 2b193f2d jdk.internal.loader.ClassLoader
s$PlatformClassLoader@1dc4f3f3
jdk.internal.loader.ClassLoaders$PlatformClassLoader@1dc4f3f3 216 1dc4f3f3 null
sun.reflect.misc.MethodUtil@775d7713 1 775d7713 jdk.internal.loader.ClassLoader
s$AppClassLoader@2b193f2d
Affect(row-cnt:5) cost in 6 ms.
## 统计 ClassLoader 实际使用 URL 和未使用的 URL
## 主要用于项目中观察哪些包没用,以及多个包冲突的时候实际加载的哪个
[arthas@11288]$ classloader --url-stat
## 通过类加载器名称指定类加载器查找类
[arthas@11288]$ classloader --classLoaderClass jdk.internal.loader.ClassLoaders$AppClassLoader -r java/lang/String.class
## 通过类加载器hash指定类加载器查找类
[arthas@11288]$ classloader -c 2b193f2d -r java/lang/String.class
jad 反编译
## 反编译指定类方法 jad --source-only 类名 方法名
[arthas@11288]$ jad --source-only java.lang.Object equals
public boolean equals(Object obj) {
/*163*/ return this == obj;
}
monitor 监控方法调用统计
文档:arthas.aliyun.com/doc/monitor…
格式:monitor 参数 类 方法 条件
## 10秒刷新一次监听PublicRestfulTpController#enumeration执行情况
[arthas@11288]$ monitor com.crykasset.yktpwx.controller.pub.PublicRestfulTpController enumeration 'params[0] =="TP_TYP E" ' -c 10
### curl -X GET "http://127.0.0.1:7091/tp/pubRf/api/enum?keys=TP_TYPE" -H "accept: application/json"
profiler 生成火焰图
文档:arthas.aliyun.com/doc/profile…
仅支持Linux或Mac,主要用于压测观察cpu或内存情况
stack 观察方法调用栈信息
文档:arthas.aliyun.com/doc/stack.h…
格式:stack 参数 类 方法 条件
## 观察当参数为TP_TYPE时PublicRestfulTpController#enumeration执行情况
[arthas@11288]$ stack com.crykasset.yktpwx.controller.pub.PublicRestfulTpController enumeration 'params[0] =="TP_TYPE" '
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 45 ms, listenerId: 2
ts=2023-10-10 12:33:26;thread_name=http-nio-7091-exec-2;id=3a;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@71c91121
@com.crykasset.yktpwx.controller.pub.PublicRestfulTpController.enumeration()
trace 追踪方法执行堆栈
文档:arthas.aliyun.com/doc/trace.h…
格式:trace 参数 类 方法 条件
## 监听PublicRestfulTpController#enumeration执行堆栈
[arthas@11288]$ trace com.crykasset.yktpwx.controller.pub.PublicRestfulTpController enumeration 'params[0] =="TP_TYPE" '
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 87 ms, listenerId: 3
`---ts=2023-10-10 12:39:56;thread_name=http-nio-7091-exec-3;id=3b;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@71c91121
`---[42.4559ms] com.crykasset.yktpwx.controller.pub.PublicRestfulTpController:enumeration()
`---[99.75% 42.3489ms ] com.crykasset.yktpwx.service.comm.EnumsService:enumeration() #40
thread 观察线程堆栈
文档:arthas.aliyun.com/doc/thread.…
watch 观察指定函数执行
文档:arthas.aliyun.com/doc/watch.h…
格式:watch 类 方法 目标Array 条件 参数
其中类和方法都支持*(星号)匹配
匹配类时默认匹配子类,可以通过两种方式排除:
1。先执行命令options disable-sub-class true关闭子类匹配
2。watch时添加参数--exclude-class-pattern xxx类全称
3。全局参数文档:arthas.aliyun.com/doc/options…
常用方式示例:
//观察PublicRestfulTpController#enumeration方法执行后的结果,展示深度为3,过滤条件为第一个参数等于TP_TYPE且执行耗时大于30毫秒
watch com.crykasset.yktpwx.controller.pub.PublicRestfulTpController enumeration "{params, returnObj, throwExp}" 'params[0] =="TP_TYPE" and #cost>30' -f -x 4
//然后 curl -X GET "http://127.0.0.1:7091/tp/pubRf/api/enum?keys=TP_TYPE" -H "accept: application/json"
/*
## 方法=方法名全称 执行结果=正常退出
method=com.crykasset.yktpwx.controller.pub.PublicRestfulTpController.enumeration location=AtExit
## 执行时间; [耗时=毫秒] 结果=以观察表达式为顺序的List[参数列表值、返回值、异常值]
ts=2023-10-09 21:34:21; [cost=571.3545ms] result=@ArrayList[
## 参数列表
@Object[][
@String[TP_TYPE],
],
## 返回值,拉高深度为4正好可以看到具体的内容
@HashMap[
@String[TP_TYPE]:@ArrayList[
@EnumVo[
code=@String[ZS],
name=@String[招商],
value=null,
summary=null,
],
@EnumVo[
code=@String[PM],
name=@String[拍卖],
value=null,
summary=null,
],
],
],
## 正常退出没有异常
null,
]
*/
tt 记录方法执行
命令语法:arthas.aliyun.com/doc/tt.html
格式:
观察:tt -t 其他参数(-n -m) 类 方法 OGNL条件表达式
检索调用记录:tt -l tt -s OGNL条件表达式
查看特定调用记录信息:tt -x 深度 -i 索引INDEX
重做特定调用记录:tt -i 索引 -p -x 深度 --replay-times 次数 --replay-interval 间隔毫秒默认1000
在特定调用记录执行表达式获取表达式结果:tt -w OGNL表达式 -x 深度 -i 索引
常用方式示例:
//观察PublicRestfulTpController#enumeration方法执行情况,记录条件为第一个参数等于TP_TYPE或TP_PM_STAGE,最大观察类1,最大观察次数3
tt -t -m 1 -n 3 com.crykasset.yktpwx.controller.pub.PublicRestfulTpController enumeration 'params[0]=="TP_TYPE" or params[0]=="TP_PM_STAGE"'
//然后
//curl -X GET "http://127.0.0.1:7091/tp/pubRf/api/enum?keys=TP_TYPE" -H "accept: application/json"
//curl -X GET "http://127.0.0.1:7091/tp/pubRf/api/enum?keys=TP_PM_STAGE" -H "accept: application/json"
//查询全部记录
tt -l
//筛选参数是TP_TYPE的记录
tt -s 'params[0]=="TP_TYPE"'
//查看索引为1000的记录,查看深度为4
tt -x 4 -i 1000
//重做索引为1000的记录并观察结果,查看深度为4
tt -p -i 1000 -x 4
//在索引1000的记录执行表达式,获得返回值map的key为TP_TYPE的值,查看深度为2
tt -w 'returnObj["TP_TYPE"]' -x 2 -i 1000
ognl 执行任意OGNL表达式
命令语法:arthas.aliyun.com/doc/ognl.ht…
格式:ognl 表达式 -x 深度 -c ClassLoader的HashCode --classLoaderClass ClassLoader的全称
常用方式示例:
//获取Spring Bean:publicRestfulTpController并执行方法enumeration
ognl -x 3 '#b=@com.crykasset.yktpwx.YkTpWxApplication@ctx.getBean("publicRestfulTpController"),#b.enumeration("TP_TYPE")'
heapdump 类似 jmap 命令的 heap dump 功能
用法:heapdump 文件路径名称如/tmp/dump.hprof
和jmap一样--live参数控制仅导出活动对象
retransform 热更新class
一般用法:retransform -c ClassLoader的HashCode class文件路径名称
指南:arthas.aliyun.com/doc/retrans…
## 热更新指定类,如果使用了自定义的类加载器,则建议指定和原类加载器相同的类加载器
## 1. 先查原类的类加载器
## JDK9及以上需要启动时添加3个参数:
## 文档:https://dev.java/learn/modules/add-exports-opens/
## --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/jdk.internal.loader=ALL-UNNAMED --add-opens java.base/java.security=ALL-UNNAMED
[arthas@15972]$ ognl '@com.crykasset.yktpwx.controller.pub.PublicRestfulTpController@class.getClassLoader().hashCode()'
@Integer[723074861]
## 2. 执行热更新
[arthas@11288]$ retransform D:/Codes/cbms-yk/yk-tp-app/target/classes/com/crykasset/yktpwx/controller/pub/PublicRestfulTpController.class
retransform success, size: 1, classes:
com.crykasset.yktpwx.controller.pub.PublicRestfulTpController
### curl -X GET "http://127.0.0.1:7091/tp/pubRf/api/enum?keys=TP_TYPE" -H "accept: application/json"