我正在调查一个奇怪的异常,当运行由使用Javassist的代理检测的java 8应用程序时>:
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;
}
如您所见,我总是返回类的未更改的 byte[]
表示形式,我不会修改任何类。
当删除带有 pool.makeClass()
的行,应用程序将正常运行。
问题:
您能给我提示一下这里出了什么问题以及 makeClass()
的哪些副作用导致了这种情况吗?
最佳答案
当您执行加载时转换时,一旦安装了转换器,就会为所有尝试加载的类调用您的 transform
方法。这包括 Javassist 本身所需的类(如果尚未加载)。因此,如果不排除这些类,就会创建循环依赖。当您尝试使用变压器仍在运行(在同一线程内)的类时,JVM 似乎会响应 NoClassDefFoundError
。
顺便说一句,如果您不更改类,我建议返回 null
来告诉 JVM 您没有更改任何内容。否则,JVM 不知道您是否已写入数组,并且必须重新解析数据(或将它们与原始字节进行比较以发现它们没有更改)。这只是一个性能问题。
关于Javassist:ClassPool.makeClass() 的副作用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33038650/