我在运行由代理检测的 java 8 应用程序时调查一个奇怪的异常,该应用程序使用 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()
导致了这一点。调用此方法时必须有一些(Classloading?)副作用
已经重现该错误的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()
引起的副作用是什么?
答案 0 :(得分:1)
执行加载时转换时,一旦安装了转换器,就会为所有尝试加载的类调用transform
方法。这包括Javassist本身所需的类,如果它们尚未加载。因此,如果不排除这些类,则会创建循环依赖项。当您尝试使用仍在运行变换器的类(在同一线程内)时,JVM似乎会对NoClassDefFoundError
作出反应。
作为旁注,如果您不更改类,我建议返回null
告诉JVM您没有更改任何内容。否则,JVM不知道您是否已写入数组并且必须重新解析数据(或将它们与原始字节进行比较以发现它们未被更改)。这只是一个性能问题。
答案 1 :(得分:0)
当我看到CallSite
时,它看起来像Java 8编译器完成的lambda魔术,支持将lambda调用点引导到invokedynamic
。有关详细信息,请参阅Javadoc LambdaMetaFactory。
Javasisst是否与Java 8完全兼容?