Loading

Arthas 热更新代码

jad & mc & redefine一条龙;更推荐 jad & mc & retransform

命令说明

mc命令

Memory Compiler/内存编译器,编译.java文件生成.class。

可以通过-c/--classLoaderClass参数指定classloader,-d参数指定输出目录

编译生成.class文件之后,可以结合retransform命令实现热更新代码。

retransform命令

加载外部的.class文件,retransform jvm已加载的类。

参考:Instrumentation#retransformClasses

retransform的限制

  • 不允许新增加field/method
  • 正在跑的函数,没有退出不能生效。

具体步骤

  • jad 命令反编译出内存中的字节码,生成 java 文件
  • 修改代码,使用 mc 命令内存编译新的 class 文件
  • retransform 重新加载新的 class 文件

1. jad反编译

jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java

2. vim修改直接修改代码

vim /tmp/UserController.java

3. sc查找加载UserController的ClassLoader

sc -d *UserController | grep classLoaderHash
$ sc -d *UserController | grep classLoaderHash
 classLoaderHash   728938a9

可以发现是 spring boot LaunchedURLClassLoader@728938a9 加载的。

注意hashcode是变化的,需要先查看当前的ClassLoader信息,提取对应ClassLoader的hashcode。

如果你使用-c,你需要手动输入hashcode:-c <hashcode>

对于只有唯一实例的ClassLoader可以通过--classLoaderClass指定class name,使用起来更加方便.

  • -classLoaderClass 的值是ClassLoader的类名,只有匹配到唯一的ClassLoader实例时才能工作,目的是方便输入通用命令,而c <hashcode>是动态变化的。

4. mc编译生成class文件

# 唯一实例的ClassLoader
mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp

# sc查找后指定
mc -c 728938a9 /tmp/UserController.java -d /tmp

output

Memory compiler output:
/tmp/com/example/demo/arthas/user/UserController.class
Affect(row-cnt:1) cost in 346 ms

5. retransform 加载class文件

retransform /tmp/com/example/demo/arthas/user/UserController.class

扩展信息

如何还原热更新代码?

查看 retransform entry

retransform -l

$ retransform -l
Id              ClassName       TransformCount  LoaderHash      LoaderClassName
1               com.example.dem 1               null            null
                o.arthas.user.U
                serController

TransformCount 统计在 ClassFileTransformer#transform 函数里尝试返回 entry对应的 .class文件的次数,但并不表明transform一定成功。

删除指定 retransform entry

retransform -d 1 (需要指定 id)

删除所有 retransform entry

retransform --deleteAll

显式触发 retransform

retransform --classPattern com.example.demo.arthas.user.UserController

消除 retransform 的影响

  • 删除这个类对应的 retransform entry (retransform -d [id])
  • 重新触发 retransform (retransform --classPattern xxxx class)

如果不清除掉所有的 retransform entry,并重新触发 retransform ,则arthas stop时,retransform过的类仍然生效。

用jad可以确认是否已经被消除

热更新检查

复杂类更新不一定成功,可以在idea中ctrl+shift+f9 检查class能够热更新。

Arthas中 class/classloader 相关命令

  • classloader - 查看 classloader 的继承树,urls,类加载信息,使用 classloader 去 getResource
  • dump - dump 已加载类的 byte code 到特定目录
  • jad - 反编译指定已加载类的源码
  • mc - 内存编译器,内存编译.java文件为.class文件
  • redefine - 加载外部的.class文件,redefine 到 JVM 里
  • retransform - 加载外部的.class文件,retransform 到 JVM 里
  • sc - 查看 JVM 已加载的类信息
  • sm - 查看已加载类的方法信息

Reference

https://github.com/alibaba/arthas/issues/537

Arthas

posted @ 2022-08-07 17:04  wheelchen  阅读(1496)  评论(0编辑  收藏  举报