Javassist:ClassPool.makeClass() 的副作用?
Javassist: Side effects of ClassPool.makeClass()?
我正在调查一个奇怪的 异常 当 运行 一个 java 8 应用程序被 agent 使用 Javassist:
Exception in thread "main" java.lang.BootstrapMethodError: java.lang.NoClassDefFoundError: Could not initialize class java.lang.invoke.CallSite
at java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(MethodHandleNatives.java:307)
at java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:297)
... 7 more
在进一步调查中,似乎是 Javassist 的 ClassPool.makeClass()
导致了这一点。调用此方法时肯定有一些(类加载?)副作用。
我的 ClassFileTransformer
的简约版本已经重现了这个错误:
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException
{
ClassPool pool = ClassPool.getDefault();
try
{
CtClass makeClass = pool.makeClass(new java.io.ByteArrayInputStream(
classfileBuffer));
}
catch (IOException | RuntimeException e)
{
e.printStackTrace();
}
return classfileBuffer;
}
如您所见,我总是返回 class 的未修改 byte[]
表示,我没有修改任何 classes。
删除带有 pool.makeClass()
的行时,应用程序运行正常。
问题:
你能告诉我这里出了什么问题吗?makeClass()
的副作用是什么导致的?
当我看到 CallSite
时,它看起来像是 Java 8 编译器完成的 lambda 魔术,以支持将 lambda 调用站点引导至 invokedynamic
。有关详细信息,请参阅 LambdaMetaFactory 的 Java 文档。
Javasisst 是否与 Java8 完全兼容?
当您执行加载时转换时,您的 transform
方法会在安装转换器后为所有试图加载的 class 调用。这包括 Javassist 本身所需的 classes,如果它们尚未加载的话。所以如果你不排除这些 classes,你就会创建一个循环依赖。当您尝试使用 class 时,JVM 似乎会对 NoClassDefFoundError
做出反应,而转换器仍然是 运行(在同一线程中)。
附带说明一下,如果您不更改 class,我建议返回 null
以告诉 JVM 您没有更改任何内容。否则,JVM 不知道你是否已经写入数组,必须重新解析数据(或将它们与原始字节进行比较,以发现它们没有改变)。这只是一个性能问题。
我正在调查一个奇怪的 异常 当 运行 一个 java 8 应用程序被 agent 使用 Javassist:
Exception in thread "main" java.lang.BootstrapMethodError: java.lang.NoClassDefFoundError: Could not initialize class java.lang.invoke.CallSite
at java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(MethodHandleNatives.java:307)
at java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:297)
... 7 more
在进一步调查中,似乎是 Javassist 的 ClassPool.makeClass()
导致了这一点。调用此方法时肯定有一些(类加载?)副作用。
我的 ClassFileTransformer
的简约版本已经重现了这个错误:
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException
{
ClassPool pool = ClassPool.getDefault();
try
{
CtClass makeClass = pool.makeClass(new java.io.ByteArrayInputStream(
classfileBuffer));
}
catch (IOException | RuntimeException e)
{
e.printStackTrace();
}
return classfileBuffer;
}
如您所见,我总是返回 class 的未修改 byte[]
表示,我没有修改任何 classes。
删除带有 pool.makeClass()
的行时,应用程序运行正常。
问题:
你能告诉我这里出了什么问题吗?makeClass()
的副作用是什么导致的?
当我看到 CallSite
时,它看起来像是 Java 8 编译器完成的 lambda 魔术,以支持将 lambda 调用站点引导至 invokedynamic
。有关详细信息,请参阅 LambdaMetaFactory 的 Java 文档。
Javasisst 是否与 Java8 完全兼容?
当您执行加载时转换时,您的 transform
方法会在安装转换器后为所有试图加载的 class 调用。这包括 Javassist 本身所需的 classes,如果它们尚未加载的话。所以如果你不排除这些 classes,你就会创建一个循环依赖。当您尝试使用 class 时,JVM 似乎会对 NoClassDefFoundError
做出反应,而转换器仍然是 运行(在同一线程中)。
附带说明一下,如果您不更改 class,我建议返回 null
以告诉 JVM 您没有更改任何内容。否则,JVM 不知道你是否已经写入数组,必须重新解析数据(或将它们与原始字节进行比较,以发现它们没有改变)。这只是一个性能问题。