redefine可动态修复线上bug,仅限修改方法体内部逻辑,适用于条件补漏、返回值修正、加日志、改硬编码;禁用类结构变更;五步完成:jad反编译→编辑→mc编译→redefine加载→watch验证。

能用 redefine 动态修复线上 Bug,前提是只改方法体内部逻辑,不碰类结构。它不是万能补丁,但对紧急边界判断错误、数值计算偏差、日志缺失这类问题,确实能做到“改完即生效”,整个过程不到两分钟。
确认是否适合热修复
redefine 只允许修改已有方法的字节码,以下情况才适用:
- 条件分支补漏(比如
if (id 漏了 <code> 的校验) - 返回值修正(如误写
return true应为return false) - 临时加一行日志或埋点(
log.debug("input: {}", param)) - 硬编码值替换(如把
TIMEOUT_MS = 3000改成5000)
以下操作会直接失败,必须避免:
- 新增或删除字段、方法
- 修改方法签名(参数类型、数量、返回值类型)
- 给类加注解、改继承关系或实现接口
- 改动
java.*或javax.*等系统类
五步完成 redefine 热修复
整个流程围绕 jad → 编辑 → mc → redefine 展开,每步都有明确目标:
-
反编译源码:执行
jad --source-only com.example.OrderService > /tmp/OrderService.java,拿到当前 JVM 正在运行的原始代码 - 本地修改保存:用 vim 或其他编辑器打开文件,仅修改出问题的方法体,保存后确保无编译错误
-
内存中编译:运行
mc -d /tmp /tmp/com/example/OrderService.java,生成标准 class 文件(路径需与包名严格一致) -
加载新字节码:执行
redefine /tmp/com/example/OrderService.class,JVM 即刻替换方法实现 -
验证效果:用
watch com.example.OrderService checkCoupon returnObj观察真实返回值,确认逻辑已生效
关键注意事项
redefine 看似简单,但几个细节决定成败:
- 目标类必须由**同一个 ClassLoader** 加载,用
sc -d com.example.OrderService查看classLoaderHash,避免多加载器冲突 - 修改前先备份原始 class:
jad --no-source com.example.OrderService > /tmp/OrderService.class.bak,便于秒级回滚 - 磁盘空间要预留至少 50MB,反编译和编译过程会产生临时文件
- 若提示
redefine failed: java.lang.UnsupportedOperationException,大概率是类被 Spring CGLIB 增强过,可尝试先用sm -d确认实际生效的方法名
替代方案:retransform 更宽松但需额外准备
如果 redefine 失败,且你已提前用 arthas-agent 启动应用,可考虑 retransform:
- 支持更宽泛的字节码变更(如部分 Lambda 表达式重写)
- 需要预先通过
Instrumentation.addTransformer注册转换器,普通 attach 模式不支持 - 生产环境建议优先走 redefine 路径,它更轻量、更可控、无需预埋











