Alibaba Arthas
基于arthas 3.4.6
Arthas是Alibaba开源的Java诊断工具
可以用来解决
-
查看class 的加载路径,排除ClassLoader 双向委派存在的问题
-
程序在线反编译,与热更新
-
监控到JVM的实时运行状态(线程状态,程序热点,内存情况)
优点:
-
兼容分享jvisualvm dump文件
-
最低支持JDK6与Linux&Mac&Windows运行(推荐不低于7)
-
命令交互,Tab 提示功能完善
-
彩色界面观察更直观
更多使用参考Arthas官方文档
常用功能分享
下载使用
官网教程链接
使用退出
使用
java -jar arthas-boot.jar
选择程序
退出
exit\quit
基础常用命令
命令界面支持常用的操作系统终端命令
help 帮助命令
pwd 当前路径
echo
类似Linux echo 但不支持输出变量
Cat
输出文件内容
cltr+l
Cls 清除终端的数据信息
history
历史输出命令
Class相关
SC 查看** JVM **已加载的类信息
sc “Search-Class” _ 的简写 用来查看 JVM 已加载的类信息 _
参数说明
参数名称 | 参数说明 |
---|---|
class-pattern | 类名表达式匹配 |
[d] | 输出当前类的详细信息,包括这个类所加载的原始文件来源、类的声明、加载的ClassLoader等详细信息。 如果一个类被多个ClassLoader所加载,则会出现多次 |
[E] | 开启正则表达式匹配,默认为通配符匹配 |
[f] | 输出当前类的成员变量信息(需要配合参数-d一起使用) |
[x:] | 指定输出静态变量时属性的遍历深度,默认为 0,即直接使用 toString 输出 |
[c:] | 指定class的 ClassLoader 的 hashcode |
[classLoaderClass:] | 指定执行表达式的 ClassLoader 的 class name |
[n:] | 具有详细信息的匹配类的最大数量(默认为100) |
class-pattern
支持 com.xxx.xx 与** com/xxx/xx **** 限定名方式 与 **** *xxx **** 通配符 模糊匹配**
-d 展示 类的详情
sc -d demo.\* (前提demo包下仅有MathGame 一个Class)
class-info demo.MathGame (全名称)
code-source /root/arthas-demo.jar (加载路径)
name demo.MathGame (全名称)
isInterface false (是否接口)
isAnnotation false (是否注解)
isEnum false (是否枚举)
isAnonymousClass false (是否匿名类)
isArray false (是否数组)
isLocalClass false (是否局部类)
isMemberClass false (是否成员类)
isPrimitive false (是否基本类型)
isSynthetic false (是否编译器引入)
simple-name MathGame (是否注解)
modifier public (可见性)
annotation (包含的注解)
interfaces (实现的接口)
super-class +-java.lang.Object
class-loader +-jdk.internal.loader.ClassLoaders$AppClassLoader@c387f44
+-jdk.internal.loader.ClassLoaders$PlatformClassLoader@3313bcd7
classLoaderHash c387f44
Affect(row-cnt:1) cost in 6 ms.
-f 展示类中字段详情配合-d
class-info demo.MathGame
code-source /root/arthas-demo.jar
name demo.MathGame
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name MathGame
modifier public
annotation
interfaces
super-class +-java.lang.Object
class-loader +-jdk.internal.loader.ClassLoaders$AppClassLoader@c387f44
+-jdk.internal.loader.ClassLoaders$PlatformClassLoader@3313bcd7
classLoaderHash c387f44
fields name random
type java.util.Random
modifier private,static
value java.util.Random@459639f
name illegalArgumentCount
type int
modifier private
Affect(row-cnt:1) cost in 4 ms.
输出当前类的详细信息,包括这个类所加载的原始文件来源、类的声明、加载的ClassLoader等详细信息。如果一个类被多个ClassLoader所加载,则会出现多次(仅限于此类被继承 存在子类 才可能被CLassLoder 加载多次)
展示此类的所有子类信息
sc 接口/抽象类
sc java.util.Map
com.sun.crypto.provider.SunJCE
com.sun.security.sasl.Provider
com.sun.security.sasl.gsskerb.JdkSASL
java.lang.ProcessEnvironment$StringEnvironment
java.security.AuthProvider
java.security.Provider
java.util.AbstractMap
java.util.Collections$EmptyMap
java.util.Collections$SingletonMap
java.util.Collections$SynchronizedMap
java.util.Collections$UnmodifiableMap
java.util.EnumMap
java.util.HashMap
java.util.Hashtable
java.util.IdentityHashMap
java.util.ImmutableCollections$AbstractImmutableMap
java.util.ImmutableCollections$MapN
java.util.LinkedHashMap
java.util.Map
java.util.NavigableMap
java.util.Properties
java.util.SortedMap
java.util.TreeMap
java.util.WeakHashMap
java.util.concurrent.ConcurrentHashMap
java.util.concurrent.ConcurrentMap
java.util.concurrent.ConcurrentNavigableMap
java.util.concurrent.ConcurrentSkipListMap
java.util.jar.Attributes
org.jcp.xml.dsig.internal.dom.XMLDSigRI
sun.net.www.http.KeepAliveCache
sun.nio.cs.StandardCharsets$Aliases
sun.nio.cs.StandardCharsets$Cache
sun.security.ec.SunEC
sun.security.jca.ProviderList$1
sun.security.jgss.SunProvider
sun.security.pkcs11.SunPKCS11
sun.security.provider.Sun
sun.security.provider.certpath.ldap.JdkLDAP
sun.security.rsa.SunRsaSign
sun.security.smartcardio.SunPCSC
sun.security.ssl.SunJSSE
sun.util.PreHashedMap
Affect(row-cnt:43) cost in 9 ms.
SM 查看已加载类的方法信息
“Search-Method” 的简写,这个命令能搜索出所有已经加载了 Class 信息的方法信息。(只能看到由当前类所声明 (declaring) 的方法,父类则无法看到)
参数信息
sc [-d 展示详情] classFullName [methodName 仅展示具体方法]
注
sm com.XX 会输出XX 以及子类的所有方法信息,如 sm java.util,Map 会展示出Map所有子类方法信息。故类名尽可能具体
Class 在线编译 & 反编译 & 热更新
jad 反编译
反编译指定已加载类的源码
其他离线反编译工具
Jad classFullName [methodName 特定函数] [–source-only 仅显示源码剔除 ClassLoader]
[arthas@7192]$ jad demo.MathGame
ClassLoader:
+-sun.misc.Launcher$AppClassLoader@18b4aac2
+-sun.misc.Launcher$ExtClassLoader@78308db1
Location:
/E:/workspace/framework/arthas/demo/target/classes/
/\*
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 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);
}
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;
}
}
|—
MC 内存编译器
Memory Compiler/ 内存编译器,编译.java 文件生成.class_。_
mc [-c 指定具体的classLoder(classLoad hashCode)] [-d 输出目录 默认当前目录]源文件1 [源文件2]
Retransform 重新加载 Class
加载外部的 .class 文件, retransform jvm 已加载的类。
retransform 在不重启的状态下,同一个Class 每次加载都会记录一条版本信息,JVM 加载的信息以日志记录的最大ID 为准
加载新的 Class
retransform filePath
加载class文件并重新加载
展示替换的版本信息
Arthas 重启后所有的信息将不会保留
retransform -l
版本信息删除
全删除
| PowerShell
retransform --deleteAll |
---|
删除特定版本
| PowerShell
retransform -d ID |
---|
基于历史记录刷新
| PowerShell
retransform --classPath classFullName[1…N] |
---|
此命令会查找历史信息中ID 最大值的class信息 ,存在的时 使用此版本的class信息替换JVM 中Class信息。若历史不存在,则回复程序原Class 中的逻辑
redefine 不可回退的热更新
其功能与 retransform功能相同,但是更新后无法回退
redefine classFilePath
| PowerShell
redefine /tmp/Test.class |
---|
推荐使用 retransform
新增日志的热更新示例
注意项
JDK 版本
Arthas 使用的 JDK与调试程序使用的JDK 相同(尽管避免Arthas 使用自动推断存在错误)
链接成功(高版本链接低版本 )
链接失败(arthas jdk 低于目标版本会失败)
补充文档
- JDWP 协议及实现 – IBM Developer