「这是我参与2022首次更文挑战的第7天,活动详情查看:2022首次更文挑战」
线上代码修改
生产环境有时会遇到非常紧急的问题,或突然发现一个bug,这时候不方便重新发版,或者发版未生效,可以使用Arthas临时修改线上代码。通过Arthas修改的步骤如下:
1. 从读取.class文件
2. 编译成.java文件
3. 修改.java文件
4. 将修改后的.java文件编译成新的.class文件
5. 将新的.class文件通过classloader加载进JVM内
第一步:读取.class文件
sc -d *DeadLockTest*
使用sc命令查看JVM已加载的类信息。关于sc命令,查看官方文档:arthas.aliyun.com/doc/sc.html
- -d : 表示打印类的详细信息
最后一个参数classLoaderHash,表示在jvm中类加载的hash值,我们要获得的就是这个值。
第二步:使用jad命令将.class文件反编译为.java文件才行
jad -c 7c53a9eb --source-only com.lxl.jvm.DeadLockTest > /Users/lxl/Downloads/DeadLockTest.java
- jad命令是反编译指定已加载类的源码
- -c : 类所属 ClassLoader 的 hashcode
- --source-only:默认情况下,反编译结果里会带有
ClassLoader信息,通过--source-only选项,可以只打印源代码。 - com.lxl.jvm.DeadLockTest:目标类的全路径
- /Users/lxl/Downloads/DeadLockTest.java:反编译文件的保存路径
/*
* Decompiled with CFR.
*
* Could not load the following classes:
* com.lxl.jvm.User
*/
package com.lxl.jvm;
import com.lxl.jvm.User;
import java.util.ArrayList;
import java.util.List;
public class DeadLockTest {
private static Object lock1 = new Object();
private static Object lock2 = new Object();
private static List<String> names = new ArrayList<String>();
private List<String> citys = new ArrayList<String>();
public static List<String> getCitys() {
DeadLockTest deadLockTest = new DeadLockTest();
/*25*/ deadLockTest.citys.add("北京");
/*27*/ return deadLockTest.citys;
}
......
public static void main(String[] args) {
......
}
}
这里截取了部分代码。
第三步:修改java文件
public static List<String> getCitys() {
System.out.println("-----这里增加了一句日志打印-----");
DeadLockTest deadLockTest = new DeadLockTest();
/*25*/ deadLockTest.citys.add("北京");
/*27*/ return deadLockTest.citys;
}
第四步:使用mc命令将.java文件编译成.class文件
mc -c 512ddf17 -d /Users/luoxiaoli/Downloads /Users/luoxiaoli/Downloads/DeadLockTest.java
- mc: 编译.java文件生.class文件, 详细使用方法参考官方文档arthas.aliyun.com/doc/mc.html
- -c:指定classloader的hash值
- -d:指定输出目录
- 最后一个参数是java文件路径
这是反编译后的class字节码文件
第五步:使用redefine命令,将.class文件重新加载进JVM
redefine -c 512ddf17 /Users/lxl/Downloads/com/lxl/jvm/DeadLockTest.class
- -c: 指定ClassLoader的hashcode
最后看到redefine success,表示重新加载.class文件进JVM成功了。
注意事项
redefine命令使用之后,再使用jad命令会使字节码重置,恢复为未修改之前的样子。官方关于redefine命令的说明
第六步:检验效果
这里检测效果,调用接口,执行日志即可。