在长时间运行的过程中,什么会导致突然的ClassNotFoundException?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (337)

我们有一个非常小的Web服务(少于1K代码行),由Jetty运行。即使在我们的压力测试阶段,这项服务也一直运行良好。然而,在13天的正常运行时间之后,我们在同一天的两个节点中遇到了ClassNotFoundException。

奇怪的是未找到的类已经存在(它是启动例程的一部分,并且经常用于处理以前的请求)。事实上,只要重新启动这个过程就可以解决这个问题。这两个节点都位于独立的机器中,并且相互独立。除了一个JMS连接之外,它们不依赖外部资源。

我在搜索时找不到相关信息,因为大多数报告的问题都与启动Java进程时类路径中的类缺失有关,这不是我们的情况。我们怀疑可能存在内存泄漏,这在某种程度上破坏了JVM内存,但这不能解释为什么相同的问题发生在大约同一时间的两个节点上。在过去五天中,我们一直在运行密集的压力测试,附加JVM监视器和内存泄漏分析器,一切看起来都很好。对于这个测试,我们将进程内存从2GB减少到512 MB。

详情:

  • 使用Java HotSpot(TM)64位服务器VM(Build 16.3-B01,混合模式)
  • 使用JettyRunner-8.1.0.RC5.jar
  • 原始cmd行:JavaXmx2048M-jar Jetty-Runner-8.1.0.RC5.jar-端口5000 webapp.war
  • Intel Xeon E5-2680 8核(X2)+16 GB内存
  • 红帽企业Linux 6
  • 一些正在使用的框架:JBossResteasy,SpringIoC,Guava。

请您提供一些想法,说明什么会使JVM突然“忘记”以前加载的类的存在,而不能再次加载它?

Caused by: java.lang.ClassNotFoundException: com.a.b.c.SomeClass
    at java.net.URLClassLoader$1.run(URLClassLoader.java:202) ~[na:1.6.0_37]
    at java.security.AccessController.doPrivileged(Native Method) ~[na:1.6.0_37]
    at java.net.URLClassLoader.findClass(URLClassLoader.java:190) ~[na:1.6.0_37]
    at java.lang.ClassLoader.loadClass(ClassLoader.java:306) ~[na:1.6.0_37]
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301) ~[na:1.6.0_37]
    at java.lang.ClassLoader.loadClass(ClassLoader.java:247) ~[na:1.6.0_37]
    at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:424) ~[na:na]
    at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:377) ~[na:na]
    at java.lang.Class.forName0(Native Method) ~[na:1.6.0_37]
    at java.lang.Class.forName(Class.java:247) ~[na:1.6.0_37]
    at sun.reflect.generics.factory.CoreReflectionFactory.makeNamedType(CoreReflectionFactory.java:95) ~[na:1.6.0_37]
    at sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:107) ~[na:1.6.0_37]
    at sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:31) ~[na:1.6.0_37]
    at sun.reflect.annotation.AnnotationParser.parseSig(AnnotationParser.java:370) ~[na:1.6.0_37]
    at sun.reflect.annotation.AnnotationParser.parseClassValue(AnnotationParser.java:351) ~[na:1.6.0_37]
    at sun.reflect.annotation.AnnotationParser.parseMemberValue(AnnotationParser.java:280) ~[na:1.6.0_37]
    at sun.reflect.annotation.AnnotationParser.parseAnnotation(AnnotationParser.java:222) ~[na:1.6.0_37]
    at sun.reflect.annotation.AnnotationParser.parseAnnotations2(AnnotationParser.java:69) ~[na:1.6.0_37]
    at sun.reflect.annotation.AnnotationParser.parseAnnotations(AnnotationParser.java:52) ~[na:1.6.0_37]
    at java.lang.reflect.Field.declaredAnnotations(Field.java:1014) ~[na:1.6.0_37]
    at java.lang.reflect.Field.getDeclaredAnnotations(Field.java:1007) ~[na:1.6.0_37]

编辑:

有人提到,在使用Win下的NFS装载时,JVM可能会决定卸载一个类,然后在需要时重新加载它。如果在此过程中NFS连接中断,则文件句柄将无效,重新加载将使用类似的堆栈跟踪失败。在我们的例子中,我们使用的是Linux,所有涉及的文件都在同一个挂载中,这是一个本地硬盘。为了进行更多的测试,我已经将CD放到Jetty临时目录中,并手动删除了一个特定服务类的著名目录。如果JVM卸载它,然后尝试从类目录重新加载它,它将失败。虽然这并不能解释最初的问题,但它可能会将更多的信息放在桌面上.

提问于
用户回答回答于

这就是正在发生的事情:

  1. 当使用上面详述的cmd启动服务时,Jetty在“/tmp”下创建一个子dir,它保存JVM加载的应用程序类和资源。
  2. 经过一段时间的不活动(在我们的特定场景中,在13到20天之间),该目录将消失。因此,JVM无法加载该文件。我们仍然不清楚JVM是否在这个错误之前卸载了类,或者为什么它试图重新读取*.类文件。了解源代码并了解这一点会很有趣,但这并不在我们的短期Todo列表中。
  3. 只需重新启动Jetty就可以重新创建丢失的目录,服务再次启动。

我们得到的一个很好的提示是,一些人在Windows上通过NFS在JAR中加载资源时报告了类似的问题(如果网络连接丢失了片刻,NFS句柄就会失效,JVM也会出现类似的错误)。这不是我们的情况(/tmp是本地存储),但非常类似。

用户回答回答于

堆栈跟踪告诉我们,它是关于处理注释的,而不是与加载类来执行代码连接的。似乎注释处理器试图解决价值通过ClassLoader带注释的元素。

换句话说,有一个具有类型值的注释,如@Foo(xyz=ABC.class)和一个类或成员用这个构造注释,但是类ABC无法通过ClassLoader在运行时对带注释的元素。

这与这个类已经通过另一个类加载的事实并不冲突。ClassLoader

所属标签

可能回答问题的人

  • 不吃貓的鱼oo

    5 粉丝466 提问6 回答
  • Richel

    8 粉丝0 提问4 回答
  • 人生的旅途

    10 粉丝484 提问3 回答
  • 发条丶魔灵1

    6 粉丝525 提问3 回答

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励