[alibaba/arthas]class redefinition failed: attempted to change the schema (add/remove fields) 分析排查

2025-11-12 688 views
4
结论

出现这个异常的原因是很明确的,在更新字节码时,增加/删除了field。可能有下面的原因:

  1. JDK的bug,尽量用最新的JDK版本,特别是JDK8

    比如: https://github.com/alibaba/arthas/issues/969#issuecomment-564394725

  2. 其它的java agent修改了字节码,增加了 field。

    比如jacoco,这个代码测试覆盖率工具,它会在类里增加一个static field来记录执行信息。

  3. 其它的java agent修改了字节码,但是在retransform时,生成同样的字节码

    比如skywalking: https://github.com/alibaba/arthas/issues/1141

  4. 在执行arthas redefine/retransform命令时,用户自己想替换的字节码有修改

    • 可能是用户本地的JDK版本和线上的JDK版本不一致导致的
    • 可能是用户自己修改了源码
理解ClassFileTransformer机制

从JDK 1.5起,有一套ClassFileTransformer的机制,Java Agent通过Instrumentation注册ClassFileTransformer,那么在类加载或者retransform时就可以回调修改字节码。

显然,在Arthas里,要增强的类是已经被加载的,所以它们的字节码都是在retransform时被修改的。 通过显式调用Instrumentation.retransformClasses(Class<?>...)可以触发回调。

Arthas里字节码相关的命令watch/trace/stack/tt/dump/jad/retransform等命令都是通过ClassFileTransformer来实现的。

ClassFileTransformer的接口如下:

public interface ClassFileTransformer {
    byte[]
    transform(  ClassLoader         loader,
                String              className,
                Class<?>            classBeingRedefined,
                ProtectionDomain    protectionDomain,
                byte[]              classfileBuffer)
        throws IllegalClassFormatException;

简而言之:

  1. transform函数传入字节码byte[],再返回新的字节码byte[]
  2. 因为JVM里注册的ClassFileTransformer可能有多个,那么在JVM里运行的字节码里,可能是被多个ClassFileTransformer处理过的。
  3. 触发了retransformClasses之后,多个agent注册的多个ClassFileTransformer会被依次回调,上一个处理的字节码传递到下一个。 所以不能保证这些ClassFileTransformer第二次执行会返回同样的结果。

所以,重点结论来了:

  1. 每个agent注册的ClassFileTransformer要支持多次执行,每次生成同样的结果。
  2. 可以在ClassFileTransformer增加field不?可以的,但要保证第一条。
  3. 如果不能保证ClassFileTransformer每次生成同样的结果,就很容易出现class redefinition failed: attempted to change the schema (add/remove fields)
  4. 有的agent实现不合理,注册ClassFileTransformer之后,做完transform之后(可能修改了schema,add/remove fields),它把注册的ClassFileTransformer删掉了。那么当别的agent再次触发retransform时,就会出错了。
排查
  1. 检查java应用有多少个java agent,可能是配置在启动参数上,也有可能是动态attach上去的
  2. 检查这些agent做了transform之后,生成的字节码是怎样的
  3. arthas自身有一个options dump true的开关,打开之后,arthas会把经过自己ClassFileTransformer处理过的字节码保存到arthas-class-dump目录,方便分析排查
  4. 利用工具把字节码dump之后分析,比如使用arthas的dump命令,或者这个工具: https://github.com/hengyunabc/dumpclass
  5. 在issue里多搜下

回答

5

我现在遇到个问题, docker里安装1.8.0_212版本的JDK,arthas3.5.5版本 exec到docker里进入arthas,执行watch命令 输出class redefinition failed: attempted to change the schema (add/remove fields) 异常。。 watch和trace都会,按说这两个命令不会更改到字节码。jad对应的方法后也不是反编译的源码,方法体不见了,输出了return (List)delegate$kilt9k0.intercept(this, new Object[0], (Callable<?>)new auxiliary.kY2cigI3(this), cachedValue$0JsIgyaN$ufneft0); 有人遇到过没有

5

@sohouse 试下最新版本arthas,jdk8也试下最新的。