Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2.14.3版本在JDK 21中遇到了类转换异常问题,TTL是否支持JDK 21? #555

Closed
brucelwl opened this issue Sep 27, 2023 · 13 comments · Fixed by #594
Closed

2.14.3版本在JDK 21中遇到了类转换异常问题,TTL是否支持JDK 21? #555

brucelwl opened this issue Sep 27, 2023 · 13 comments · Fixed by #594
Assignees
Labels
dependencies Pull requests that update a dependency file 💪 enhancement ttl agent

Comments

@brucelwl
Copy link

2.14.3版本是不是还不支持JDK21??

@wuwen5
Copy link
Collaborator

wuwen5 commented Sep 28, 2023

从目前的CI构建中看,是覆盖了JDK21的,覆盖了JDK21环境的测试:

建议你反馈你在jdk21环境中你遇到了什么问题,提供相关有效的信息。

@wuwen5 wuwen5 added the ❓question Further information is requested label Sep 28, 2023
@brucelwl
Copy link
Author

brucelwl commented Sep 28, 2023

@wuwen5 异常信息如下, 这个问题在Idea中启动ttl 的 agent能够复现, 以命令方式挂载agent启动没有问题, 猜测是和idea的debug agent有冲突

2023-09-28 10:53:34.927 SEVERE [main] TtlTransformer: Fail to transform class sun/net/httpserver/ServerImpl$ReqRspTimeoutTask, cause: java.lang.NullPointerException: Cannot read field "string" because "utf" is null
java.lang.NullPointerException: Cannot read field "string" because "utf" is null
	at com.alibaba.ttl.threadpool.agent.internal.javassist.bytecode.ConstPool.getUtf8Info(ConstPool.java:675)
	at com.alibaba.ttl.threadpool.agent.internal.javassist.bytecode.MethodParametersAttribute.copy(MethodParametersAttribute.java:90)
	at com.alibaba.ttl.threadpool.agent.internal.javassist.bytecode.AttributeInfo.copyAll(AttributeInfo.java:249)
	at com.alibaba.ttl.threadpool.agent.internal.javassist.bytecode.MethodInfo.compact(MethodInfo.java:157)
	at com.alibaba.ttl.threadpool.agent.internal.javassist.bytecode.ClassFile.compact(ClassFile.java:242)
	at com.alibaba.ttl.threadpool.agent.internal.javassist.CtClassType.toBytecode(CtClassType.java:1583)
	at com.alibaba.ttl.threadpool.agent.internal.javassist.CtClass.toBytecode(CtClass.java:1519)
	at com.alibaba.ttl.threadpool.agent.TtlTransformer.transform(TtlTransformer.java:81)
	at java.instrument/java.lang.instrument.ClassFileTransformer.transform(ClassFileTransformer.java:244)
	at java.instrument/sun.instrument.TransformerManager.transform(TransformerManager.java:188)
	at java.instrument/sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:610)
	at java.base/java.lang.ClassLoader.defineClass2(Native Method)
	at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1118)
	at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:182)
	at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:821)
	at java.base/jdk.internal.loader.BuiltinClassLoader.findClassInModuleOrNull(BuiltinClassLoader.java:741)
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:665)
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526)
	at jdk.httpserver/sun.net.httpserver.HttpServerImpl.<init>(HttpServerImpl.java:50)
	at jdk.httpserver/sun.net.httpserver.DefaultHttpServerProvider.createHttpServer(DefaultHttpServerProvider.java:35)
	at jdk.httpserver/com.sun.net.httpserver.HttpServer.create(HttpServer.java:152)
	at jdk.httpserver/com.sun.net.httpserver.HttpServer.create(HttpServer.java:126)
	at io.prometheus.client.exporter.HTTPServer.<init>(HTTPServer.java:144)
	at io.prometheus.client.exporter.HTTPServer.<init>(HTTPServer.java:165)

@wuwen5 wuwen5 added ttl agent 🐞 bug Something isn't working labels Oct 8, 2023
@wuwen5 wuwen5 self-assigned this Oct 8, 2023
@wuwen5 wuwen5 added dependencies Pull requests that update a dependency file and removed ❓question Further information is requested labels Oct 8, 2023
@wuwen5
Copy link
Collaborator

wuwen5 commented Oct 8, 2023

应该是javassist在处理 sun/net/httpserver/ServerImpl$ReqRspTimeoutTask 字节码的时候出现了问题。

已提交到javassist

@wuwen5
Copy link
Collaborator

wuwen5 commented Oct 8, 2023

知道怎么回事了,问题出现在内部类的字节码发生了细微变化,在java8之后,当使用 -parameters 选项编译Java代码时,编译器会将方法参数的名称存储在类文件的调试信息中。这样,当你使用调试器来调试程序时,可以看到方法的参数名称而不仅仅是它们的类型。

使用-parameters编译时,class信息如下 (当未使用-parameters编译时,不会携带MethodParameters)

public void test(int, java.lang.String);
    descriptor: (ILjava/lang/String;)V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=0, locals=3, args_size=3
         0: return
      LineNumberTable:
        line 4: 0
    MethodParameters:
      Name                           Flags
      i
      s

默认在jdk中的class应该是没有携带-parameters编译的,所以默认不会携带MethodParameters,目前查看class也是这样。
但是在Java21中编译内部类时,即使没携带-parameters编译时,构造函数仍然会携带MethodParameters,这与其他版本jdk字节码不一致,这也是不合理和非预期的地方。

当使用-parameters编译含内部类时,jdk8、jdk17、jdk21对MethodParameters处理一致,如下(有一个参数,参数名称为this$0)

test4.MethodParamInnerClassTest$InnerClass(test4.MethodParamInnerClassTest);
    descriptor: (Ltest4/MethodParamInnerClassTest;)V
    flags: (0x0000)
    Code:
      ()
    MethodParameters:
        Name                           Flags
        this$0                         final mandated

当未使用-parameters编译含内部类时,jdk8\jdk17如下,没有MethodParameters

test4.MethodParamInnerClassTest$InnerClass(test4.MethodParamInnerClassTest);
    descriptor: (Ltest4/MethodParamInnerClassTest;)V
    flags: (0x0000)
    Code:
      ()

而java21如下,仍然携带了MethodParameters,表示有一个参数,但无法获取参数名称 (<no name>)。

test4.MethodParamInnerClassTest$InnerClass(test4.MethodParamInnerClassTest);
    descriptor: (Ltest4/MethodParamInnerClassTest;)V
    flags: (0x0000)
    Code:
      ()
    MethodParameters:
      Name                           Flags
      <no name>                      final mandated

所以,总结为,java21编译的内部类字节码发生了变化,引起的兼容性问题。

javassist在解析java21编译的内部类字节码时,在对MethodParameters字节码的解析时无法从常量池中获取字符信息。
大致可以这么理解,字节码告诉了构造方法有一个参数,参数名对应常量池表的下标为0,常量池表信息从下标1开始,0为jvm保留(具体不详 // index 0 is reserved by the JVM.),即无法从常量池获取字符串信息。

JVM规范:

@brucelwl
Copy link
Author

brucelwl commented Oct 8, 2023

@wuwen5 是不是意味着即使用jdk8 在添加-parameters参数的情况下,也会触发这个异常?

@wuwen5
Copy link
Collaborator

wuwen5 commented Oct 8, 2023

@wuwen5 是不是意味着即使用jdk8 在添加-parameters参数的情况下,也会触发这个异常?

不会

添加-parameters参数时,无论是java21还是java8都会正确符合预期的带上MethodParameters 信息,此时javassist能正常解析。

现在问题是没使用-parameters参数时,预期是所有方法都不会带MethodParameters,但是java21却在内部类的构造方法中带了没有名称的MethodParameters

@brucelwl
Copy link
Author

brucelwl commented Oct 8, 2023

@wuwen5 目前看来只能在javassist层面兼容这个问题, 或者反馈给JDK确认是否是预期之类的更改

@wuwen5
Copy link
Collaborator

wuwen5 commented Oct 8, 2023

先记录下可能与之相关的信息

@wuwen5 wuwen5 added 💪 enhancement and removed 🐞 bug Something isn't working labels Oct 9, 2023
@wuwen5 wuwen5 changed the title 2.14.3版本是不是还不支持JDK21?? 2.14.3版本在JDK-21 中遇到了类转换异常问题,transmittable-thread-local是否支持JDK 21? Oct 10, 2023
@wuwen5 wuwen5 changed the title 2.14.3版本在JDK-21 中遇到了类转换异常问题,transmittable-thread-local是否支持JDK 21? 2.14.3版本在JDK-21 中遇到了类转换异常问题,ttl是否支持JDK 21? Oct 10, 2023
@wuwen5
Copy link
Collaborator

wuwen5 commented Oct 10, 2023

进一步研究发现,影响范围为java21编译出来的非静态的内部类

非静态内部类, 包含MethodParameters <no name>

Java21InnerClassWithoutParameters$InnerClass(Java21InnerClassWithoutParameters);
    descriptor: (LJava21InnerClassWithoutParameters;)V
    flags: (0x0000)
    Code:
      stack=1, locals=2, args_size=2
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 2: 0
    MethodParameters:
      Name                           Flags
      <no name>                      final mandated

静态内部类,不含MethodParameters

Java21InnerClassWithoutParameters$StaticInnerClass();
    descriptor: ()V
    flags: (0x0000)
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 7: 0

@liach
Copy link

liach commented Nov 3, 2023

虚拟机规范允许MethodParameters里面项目名称指向0,表示参数没有名称。javaassist没有考虑这种情况,因为之前好像没有编译器在里面放0所以实际使用时javaassist没出过错。你可以编译个带Optional参数的enum:

enum TestEnum {
  INSTANCE(Optional.of("A"));
  TestEnum(Optional<String> opt) {}
}

也会受影响,因为编译器加了name ordinal两个参数,类似inner class有个outer this参数。
这个改动主要是为了修这个例子里面的一个bug:本来constructor.getParameters()[2].getParameterizedType()返回是Optional.class,这个改动后因为编译器加信息,会返回代表Optional<String>Type

@wuwen5
Copy link
Collaborator

wuwen5 commented Dec 20, 2023

当前已发布的javassist-3.30.1-GA已经修复了这个问题,但是由于合入了其他修改,编译的字节码升级到了Java 11,因此3.30.1-GA这个版本我们暂时还不能直接升级,需等待下个版本发布.

@oldratlee
Copy link
Member

oldratlee commented Dec 20, 2023

当前已发布的javassist-3.30.1-GA已经修复了这个问题,但是由于合入了其他修改,编译的字节码升级到了JDK-11

@wuwen5

我也发现了javassist-3.30.1-GA升级到Java 11 😰

看到你在推进让javassist新版继续支持Java 8了 🚀👍

@oldratlee oldratlee changed the title 2.14.3版本在JDK-21 中遇到了类转换异常问题,ttl是否支持JDK 21? 2.14.3版本在JDK 21中遇到了类转换异常问题,TTL是否支持JDK 21? Dec 20, 2023
@oldratlee
Copy link
Member

@brucelwl @wuwen5 @liach

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dependencies Pull requests that update a dependency file 💪 enhancement ttl agent
Projects
None yet
4 participants