四、Arthas
1、安装/卸载
(1)、Arthas支持在Linux/Unix/Mac等平台上一键安装,请复制以下内容,并粘贴到命令行中,敲回车执行即可。
curl -L https://arthas.aliyun.com/install.sh | sh
上述命令会下载启动脚本文件as.sh到当前目录,你可以放在任何地方或将其加入到$PATH中。直接在shell下面执行./as.sh,就会进入交互界面。也可以执行./as.sh -h来获取更多参数信息。
(2)、卸载
在Linux/Unix/Mac平台删除下面文件。
rm -rf ~/.arthas/
rm -rf ~/logs/arthas
2、运行
执行:java -jar arthas-boot.jar 运行Arthas,粘附到指定Java应用进程
3、命令
(1)、基础命令
help——查看命令帮助信息
cls——清空当前屏幕区域
session——查看当前会话的信息
reset——重置增强类,将被 Arthas 增强过的类全部还原,Arthas 服务端关闭时会重置所有增强过的类
version——输出当前目标 Java 进程所加载的 Arthas 版本号
history——打印命令历史
quit——退出当前 Arthas 客户端,其他 Arthas 客户端不受影响
stop——关闭 Arthas 服务端,所有 Arthas 客户端全部退出
keymap——Arthas快捷键列表及自定义快捷键
(2)、常用命令
命令列表:命令列表 | arthas
①、dashboard 查看仪表盘,按Q或Ctrl + C可以中断执行。
②、thread 获取该进程的所有线程
thread 1 查看序号为1线程的内容
③、jad java.lang.Object 反编译Object类方便查看
4、源码分析
(1)、arthas-boot模块
①、输入命令:java -jar arthas-boot.jar 或 ./as.sh
②、运行arthas-boot.jar中com.taobao.arthas.boot.Bootstrap#main方法,或者运行as.sh文件
③、获取arthas版本信息
④、解析命令传入参数,将参数解析成CommandLine对象
⑤、设置镜像仓库,日志打印等
⑥、端口校验是否被占用
⑦、获取pid,校验目标端口的pid和attach pid是否一致
⑧、获取arthas home,启动arthas-core.jar
(2)、arthas-core模块
①、运行arthas-boot.jar中com.taobao.arthas.core.Arthas#main
②、将透传的参数解析转换成CommandLine对象
③、获取VirtualMachine.list()列表,匹配目标进程pid
④、利用VirtualMachine.attach(),attach到目标进程
⑤、利用virtualMachine.loadAgent()方法加载agent的jar包arthas-agent.jar
(3)、arthas-agent模块(从这开始所有代码都是运行在目标JVM中)
①、运行arthas-agent.jar中com.taobao.arthas.agent334.AgentBootstrap#main
②、对传递的参数解析
③、通过反射执行com.taobao.arthas.core.server.ArthasBootstrap#getInstance(Instrumentation ins, String args)方法,初始化spy和arthas命令
④、将arthas-spy.jar添加到BootstrapClassLoader加载目录下,解决一些ClassLoader加载不到SpyAPI的问题,加载SpyAPI,通过字节码插入到业务代码的逻辑类在arthas-spy.jar包
⑤、初始化arthas运行的环境变量
(4)、arthas-spy.jar包
①、SpyAPI当中的6个“at…”静态方法都调用了实例spyInstance中对应的方法。spyInstance的静态类型是抽象类AbstractSpy,抽象类AbstractSpy当中有六个抽象方法。当SpyAPI被加载时,它的静态语句会将spyInstance设置为类NopSpy的实例,其中这NopSpy继承自AbstractSpy并且实现的每个方法都是空操作。
②、AbstractSpy抽象类
- public static abstract class AbstractSpy {
- // 进入
- public abstract void atEnter(Class> clazz, String methodInfo, Object target, Object[] args);
- // 退出
- public abstract void atExit(Class> clazz, String methodInfo, Object target, Object[] args, Object returnObject);
- // 异常退出
- public abstract void atExceptionExit(Class> clazz, String methodInfo, Object target, Object[] args, Throwable throwable);
- // 调用前
- public abstract void atBeforeInvoke(Class> clazz, String invokeInfo, Object target);
- // 调用后
- public abstract void atAfterInvoke(Class> clazz, String invokeInfo, Object target);
- // 异常调用
- public abstract void atInvokeException(Class> clazz, String invokeInfo, Object target, Throwable throwable);
- }
③、Enhancer类
对类进行通知增强,继承ClassFileTransformer,并实现transform方法
- public class Enhancer implements ClassFileTransformer {
- // 通过静态代码块初始化SpyAPI
- static {
- SpyAPI.setSpy(spyImpl);
- }
-
- @Override
- public byte[] transform(final ClassLoader inClassLoader, String className, Class> classBeingRedefined,
- ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
- try {
- // 检查classloader能否加载到 SpyAPI,如果不能,则放弃增强
- try {
- if (inClassLoader != null) {
- inClassLoader.loadClass(SpyAPI.class.getName());
- }
- } catch (Throwable e) {
- logger.error("the classloader can not load SpyAPI, ignore it. classloader: {}, className: {}",
- inClassLoader.getClass().getName(), className, e);
- return null;
- }
- }
- ...
- return null;
- }
- }
参考文章:
(1)、消失的堆栈:消失的堆栈
(2)、JVM Attach机制:JVM Attach机制实现 - 你假笨
(3)、Arthas原理一:Arthas原理系列(一):利用JVM的attach机制实现一个极简的watch命令
(4)、Arthas原理二:Arthas原理系列(二):总体架构和项目入口
(5)、Arthas原理三:Arthas原理系列(三):服务端启动流程