我有一个有趣的问题-如果我的应用程序运行很长时间(>20h),那么有时我会得到NoClassDefFound错误-似乎JVM决定无论如何都不使用该类,并对其进行GCd。
更具体地说,这里有一个例子:
object ErrorHandler extends PartialFunction[Throwable,Unit] {
def isDefinedAt(t: Throwable) = true
def apply(e: Throwable) =e match {
// ... handle errors
}
}
// somewhere else in the code...
try {
// ... long running code, can take more than 20 hours to complete
} catch (ErrorHandler)
我得到以下例外:
Exception in thread "main" java.lang.NoClassDefFoundError: org/rogach/avalanche/ErrorHandler$
如果try/catch块运行的时间较短,那么一切都会按预期进行。
如果有人感兴趣,这里是有问题的代码库:雪崩
我需要注意的是,我只在使用JRE 6u26和Scala 2.9.1/2.9.2的Cent OS 5
机器上看到了这个和类似的问题。
这个问题的原因可能是什么?
如果您在尝试初始化类时内存不足,您认为会看到OutOfMemory还是NoClassDef?
//from initialize_impl
if (NULL == message) {
// Out of memory: can't create detailed error message
THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), className);
您的代码可能抛出OOM,然后无法加载异常处理程序对象。
当然,还有其他可能的瞬态条件:网络中断,类在网络驱动器上;或者在测试期间清理了类目录。另一种可能性是,您在不区分大小写的文件系统上构建了应用程序,并在具有异常命名的类文件的区分大小写文件系统上进行测试。例如,如果您在不删除*.class的情况下将对象"handler"更改为"handler",您仍然会看到"handler.class"
我还没有机会尝试破坏AbstractFileClassLoader;我之前的推测如下:
值得解释的是,Avalanche是一个构建工具,您可以运行scalac的一个实例来将build.scala编译为内存中的类文件,该类文件由scalac的AbstractFileClassLoader加载,其中"AbstractFile"是抽象。由于任何build.scala都会干扰工具配置,因此AFCL尊重类加载器委托显然是至关重要的。这似乎是真的,但我注意到它并没有首先委托给getResourceAsStream上的父级(快速测试证实了这一点)。可疑的是,findClass使用classBytes,它在失败时调用super,这是一个很好的ScalaClassLoader,但它使用getResourceAsStream来加载Foo.class。因此,调用findClass可能会从父CL返回一个类(这是错误的),尽管如果已知父CL已经失败,这可能是没有意义的。因为现在是半夜,我无法得出结论,但如果我的构建工具依赖于这种行为,我想明确这一点。
我不知道你的build.scala(或av.scala)中运行了一天的是什么,但也许你的Avalanche(重新)被一个行为不端的子类加载器加载了,然后当它抛出时,CL无法找到Class你的ErrorHandler。