什么可能在长时间运行的进程中导致突然的ClassNotFoundException?

Seb*_*ian 16 java jvm noclassdeffounderror classnotfoundexception

我们有一个非常小的Web服务(少于1K行代码)由Jetty运行.即使在压力测试阶段,该服务也始终如一.但是,经过13天的正常运行时间后,我们在同一天的两个节点中遇到了ClassNotFoundException.

奇怪的是,找不到的类已经存在(它是启动例程的一部分,并且它不断用于以前的请求).事实上,只需重新启动流程即可解决问题.两个节点都在不同的机器中,并且彼此独立.它们不依赖于外部资源,除了一个JMS连接.

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

细节:

  • 使用Java HotSpot(TM)64位服务器VM(内置16.3-b01,混合模式)
  • 使用jetty-runner-8.1.0.RC5.jar
  • 原始cmd行:java -Xmx2048M -jar jetty-runner-8.1.0.RC5.jar --port 5000 webapp.war
  • Intel Xeon E5-2680 8核(x2)+ 16GB RAM
  • 红帽企业Linux 6
  • 正在使用的一些框架:JBoss Resteasy,Spring IoC,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]
Run Code Online (Sandbox Code Playgroud)

编辑:

有人提到我,在Win下使用NFS挂载时,JVM可能会决定卸载一个类,然后在需要时重新加载它.如果在此过程的中间,NFS连接被破坏,则文件句柄将无效,并且重新加载将失败并具有类似的堆栈跟踪.在我们的例子中,我们使用的是Linux,所有涉及的文件都在同一个mount中,这是一个本地硬盘.只是为了进行更多测试,我已将CD刻录到Jetty临时目录中,并手动删除了一个特定服务类的知名人员.如果JVM卸载它然后尝试从classes目录重新加载它,它将失败.虽然这不能解释原始问题,但可能会在表格中提供更多信息......

Seb*_*ian 6

这就是发生的事情:

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

我们得到的一个很好的提示是,有些人在Windows上通过NFS在JAR中加载资源时报告了类似的问题(如果网络连接短暂丢失,NFS处理失效,JVM失败并出现类似错误).这不是我们的情况(/ tmp是本地存储),但非常相似.

谢谢大家的帮助.