java - 服务器非托管线程中出现无法解释的 ClassNotFoundException

标签 java classloader classnotfoundexception websphere-8 apache-commons-io

在以下情况下,我遇到一个奇怪的 ClassNotFoundException: org.apache.commons.io.IOUtils:

我有一个在 Websphere Application Server 8.0 中运行的 Web 应用程序,它使用 commons-io 2.0.1(此 jar 已正确放置在类路径中)。

我正在使用 org.apache.commons.io.input.Tailer 来跟踪日志文件并在屏幕上显示其内容。 Tailer 实现了 Runnable,因此它在自己的线程中运行。

在应用程序关闭时,我调用 tailer.stop() 来停止线程。然后 Tailer 执行的最后一件事是最后一个 block ,它在其中关闭日志读取器。

} finally {
    IOUtils.closeQuietly(reader);
}

当执行该行时,会出现 ClassNotFoundException: org.apache.commons.io.IOUtils

我做了一些测试,将一些代码添加到 Tailer 类中(并在 jar 中替换它)以确定是否是 ClassLoader 问题:

<强>1。确定 Tailer 的资源:

Tailer.class.getClassLoader().getResource("org/apache/commons/io/input/Tailer.class");

Output: wsjar:file:/home/myuser/.m2/repository/commons-io/commons-io/2.0.1/commons-io-2.0.1.jar!/org/apache/commons/io/input/Tailer.class which is the right jar

<强>2。它使用什么类加载器?: Tailer.class.getClassLoader();

com.ibm.ws.classloader.CompoundClassLoader@8d336acd[war:application/myApp.war] Again this is correct.

<强>3。尝试直接从之前的ClassLoader加载IOUtils: Tailer.class.getClassLoader().loadClass("org.apache.commons.io.IOUtils");

Again a ClassNotFoundException

<强>4。现在,最令人惊讶的事情是:如果我在 run 方法的开头添加如下所示的 Class.forName() ,则该类将正确加载,并且现在 finally block 中的 IOUtils 调用可以工作。怎么会呢?

public void run() {
     RandomAccessFile reader = null;
     try {
         Class.forName("org.apache.commons.io.IOUtils");
     }catch(Exception e){
         e.printStackTrace();
     }         
     try {
         ...
         while (run) {
            ...
         }
     } catch (Exception e) {
         listener.handle(e);
     } finally {
         IOUtils.closeQuietly(reader);
     }
 }

 /**
 * Allows the tailer to complete its current loop and return.
 */
 public void stop() {
     this.run = false;
 }

这没有任何意义!它使用 commons-io-2.0.1.jar 中的 Tailer,但它不能使用位于同一个 jar 内的类。但是,如果您在 run() 开始时强制加载该类,那么它就不会再失败。有什么想法吗?

这是整个堆栈跟踪:

Exception in thread "Thread-88" java.lang.NoClassDefFoundError: org.apache.commons.io.IOUtils
    at org.apache.commons.io.input.Tailer.run(Tailer.java:319)
    at java.lang.Thread.run(Thread.java:784)
Caused by: java.lang.ClassNotFoundException: org.apache.commons.io.IOUtils
    at java.net.URLClassLoader.findClass(URLClassLoader.java:434)
    at com.ibm.ws.bootstrap.ExtClassLoader.findClass(ExtClassLoader.java:230)
    at java.lang.ClassLoader.loadClassHelper(ClassLoader.java:703)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:682)
    at com.ibm.ws.bootstrap.ExtClassLoader.loadClass(ExtClassLoader.java:123)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:665)
    at com.ibm.ws.classloader.ProtectionClassLoader.loadClass(ProtectionClassLoader.java:62)
    at com.ibm.ws.classloader.ProtectionClassLoader.loadClass(ProtectionClassLoader.java:58)
    at com.ibm.ws.classloader.CompoundClassLoader.loadClass(CompoundClassLoader.java:566)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:665)
    at com.ibm.ws.classloader.CompoundClassLoader.loadClass(CompoundClassLoader.java:566)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:665)
    ... 2 more

最佳答案

第 #1 项和第 #2 项只是数据点,表明常见问题并不是原因。它们不一定表明问题的根源。

第 3 项说明了该问题。看起来基于该项目,此时类已经从内存中卸载。我查阅了一些文档,这些文档显示了 ClassLoader 在什么条件下会抛出异常,但找不到任何文档。因此,我猜测类已被卸载,并且应用程序的状态使得 warfile 本身已被释放。进一步的猜测是无关紧要的。

项目 #4 确实很好奇。 Class.forName 的文档说“Class 类的实例表示正在运行的 Java 应用程序中的类和接口(interface)”。 “Class 没有公共(public)构造函数。相反,Class 对象是在加载类时由 Java 虚拟机自动构造的,并通过调用类加载器中的 DefineClass 方法来构造。”。这似乎意味着即使该类已从内存中卸载,Class.forName 也应该能够加载它。

在您的示例中,Item #4 在finally block 中没有Class.forName。我认为这只是粘贴代码的一个产物,并且您实际上将其放在那里并引发了错误。

更新:与OP讨论后,以下建议可以完成这项工作。 在调用 tailer.stop() 时,在调用后放置 thread.sleep() 几秒钟。在应用程序从类加载器卸载之前,这应该留出足够的时间来完成finally调用(closeQuietly(reader))。

关于java - 服务器非托管线程中出现无法解释的 ClassNotFoundException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28855929/

相关文章:

java - 如何根据条件设置rest-api调用之间的等待时间

maven - com.mysql.cj.jdbc.Driver、MySQL Connector 和 IntelliJ IDEA 出现 ClassNotFoundException

java - 在 MATLAB 中解析 XML 字符串

java - 当 Json 在 Play Framework 中太大时,Json 为 null

java - 外部类加载Java类的内存限制

java - 用于 Hibernate 映射的自定义类加载器

java - 使用 javax.tool 进行级联内存编译

Java-Web-Start 在我的应用程序中创建 docx 文件时出现 NoClassDefFoundError

java - 在我的 Jar 文件中找不到类

java - 突出显示 ListView 项目而不触摸它