调试器里改代码为什么有时能热更新、有时必须重启?答案就在 JVM 的类重定义能力里。这篇用 Instrumentation.redefineClasses 把每一种修改试一遍,再看已有实例和正在执行的方法会怎样(Instrumentation 与 JVMTI 的基础见前两篇)。
只改方法体(calc 改成 a * 100,name 返回 v2-name) → 成功 新增一个方法 → UnsupportedOperationException: class redefinition failed: attempted to add a method 新增一个字段 → UnsupportedOperationException: class redefinition failed: attempted to change the schema (add/remove fields) 删除一个方法 → UnsupportedOperationException: class redefinition failed: attempted to delete a method 改方法签名(calc(int) → calc(long)) → UnsupportedOperationException: class redefinition failed: attempted to delete a method 改方法的访问修饰符(public → private) → UnsupportedOperationException: class redefinition failed: attempted to change method modifiers 让类实现一个接口 → UnsupportedOperationException: class redefinition failed: attempted to change superclass or interfaces 改 static 方法体 → 成功
重定义之前:name() = v1-name,calc(5) = 6,st() = v1-static 重定义(只改方法体)之后,同一个对象 o:name() = v2-name,calc(5) = 500(已有的实例也用上了新方法体,字段 x 的值保持:1) 再用另一版重定义 static 方法体:st() = v2-static,name() 变回 v1-name(每次重定义都是用你给的整份字节码替换) 正在运行的 Slow.run()(在 sleep 的中途被重定义):这次调用返回 旧代码前半 + 旧代码后半(已经在执行的那次仍然跑完旧代码);重定义之后新的调用 Slow.run() = 新代码前半 + 新代码后半