(菜鸟程序员)今天在公司写代码,写好后激动的在测试环境上线,预期着代码优雅流畅的执行,心里美滋滋。在自动部署平台上点击了一键发布,然后盯着日志直到看到下面这个玩意出现,启动成功。
然后我登陆web页面,惊奇的发现nginx竟然报错,始料未及:
由于是前后端分离的错误,因此我第一反应是前端的问题。这个页面是50x.html的错误页面,于是进入到nginx配置页面想看看到底是什么问题,然后发现下面的配置:
500,502,503,504这几个错误的code都和后端有关系,于是我不得不还是怀疑自己后端的代码问题,测试环境已经由于这个原因无法使用,怀着忐忑的心情我去查询应用生成的日志。看了一些日志,出现大量类似如下出错:
Exception in thread "commons-pool-EvictionTimer" java.lang.NoClassDefFoundError: org/apache/commons/pool2/impl/DefaultPooledObject
at redis.clients.jedis.JedisFactory.makeObject(JedisFactory.java:121)
at org.apache.commons.pool2.impl.GenericObjectPool.create(GenericObjectPool.java:868)
at org.apache.commons.pool2.impl.GenericObjectPool.ensureIdle(GenericObjectPool.java:927)
at org.apache.commons.pool2.impl.GenericObjectPool.ensureMinIdle(GenericObjectPool.java:906)
at org.apache.commons.pool2.impl.BaseGenericObjectPool$Evictor.run(BaseGenericObjectPool.java:1046)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)
Caused by: java.lang.ClassNotFoundException: Illegal access: this web application instance has been
stopped already. Could not load [org.apache.commons.pool2.impl.DefaultPooledObject]. The following
stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.
抛出的错误"java.lang.NoClassDefFoundError”,造成的原因是 Illegal access: this web application instance has been stopped already. Could not load......。
一、java.lang.NoClassDefFoundError错误
1、原因
NoClassDefFoundError错误的发生,是因为Java虚拟机在编译时能找到合适的类,而在运行时不能找到合适的类导致的错误。例如在运行时我们想调用某个类的方法或者访问这个类的静态成员的时候,发现这个类不可用,此时Java虚拟机就会抛出NoClassDefFoundError错误。与ClassNotFoundException的不同在于,这个错误发生只在运行时需要加载对应的类不成功,而不是编译时发生。很多Java开发者很容易在这里把这两个错误搞混。
java.lang.ClassNotFoundException和java.lang.NoClassDefFoundError有明显的区别。NoClassDefFoundError发生在JVM在动态运行时,根据你提供的类名,在classpath中找到对应的类进行加载,但当它找不到这个类时,就发生了java.lang.NoClassDefFoundError的错误,而ClassNotFoundException是在编译的时候在classpath中找不到对应的类而发生的错误。ClassNotFoundException比NoClassDefFoundError容易解决,是因为在编译时我们就知道错误发生,并且完全是由于环境的问题导致。
2、解决
根据前文,很明显NoClassDefFoundError的错误是因为在运行时类加载器在classpath下找不到需要加载的类,所以我们需要把对应的类加载到classpath中,或者检查为什么类在classpath中是不可用的,这个发生可能的原因如下:
- 对应的Class在java的classpath中不可用
- 你可能用jar命令运行你的程序,但类并没有在jar文件的manifest文件中的classpath属性中定义
- 可能程序的启动脚本覆盖了原来的classpath环境变量
- 因为NoClassDefFoundError是java.lang.LinkageError的一个子类,所以可能由于程序依赖的原生的类库不可用而导致
- 检查日志文件中是否有java.lang.ExceptionInInitializerError这样的错误,NoClassDefFoundError有可能是由于静态初始化失败导致的
- 如果你工作在J2EE的环境,有多个不同的类加载器,也可能导致NoClassDefFoundError
二、Illegal access: this web application instance has been stopped already. Could not load
关于这个问题,在网上查找了大量解决方案,有两种:
1、查找导致该问题的根源,然后修改源头
在WEB-INF/classes目录下新建logging.properties,将下面内容复制到新建的文件中,然后重新启动tomcat,这个时候tomcat会打印出比较详细的异常信息,再根据这些异常信息去解决。
handlers = org.apache.juli.FileHandler, java.util.logging.ConsoleHandler
############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################
org.apache.juli.FileHandler.level = FINE
org.apache.juli.FileHandler.directory = ${应用目录}/logs
org.apache.juli.FileHandler.prefix = error-debug.
java.util.logging.ConsoleHandler.level = FINE
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
2、可能是Tomcat内存泄漏
这个是在关闭应用服务器或重新部署装载项目失败会发生。当应用程序卸载时,并不会关闭所有的线程。当tomcat已经关闭了其类加载器后,一些线程依然会继续运行,这样就导致出错,这些错误就会被到日志文件里。 解决方案就是,修改tomcat/conf目录下的server.xml中的reloadable="false"修改为reloadable="true"。
<Context path="" docBase="/home/appops/media-push-dev/push-admin-dev/default/webroot" reloadable="false" ......(省略)/>
上述问题和解决方案是今天在遇到问题的后,浏览网上的内容总结出来的。
参考文章 https://blog.youkuaiyun.com/jamesjxin/article/details/46606307
但是,我并不是按照上述方案解决的,绕了一大圈,排查了一上午,我才发现
@Value("${device.recover.url}")
private String recoverUrl;
结论:这个地方的@Value("${device.recover.url}"),压根就没有获取到值,application.properties没有这个字段的值,所以spring容器启动失败,才导致了后续的问题,真的是坑爹啊。之所以在最开始没有发现在这个问题,是因为应用启动后出现了Springboot的字样(第一张图)后,紧接着就打印出了关于"${device.recover.url}"取不到值的错误,但是后面又飞速的打印出了上述this web application instance has been stopped already 和 java.lang.NoClassDefFoundError的错误,将最有用的信息刷没了,把我的注意力一直吸引到这两个错误上。两个教训:1.粗心大意,遗漏了重要的打印信息,而导致了大量的时间消耗;2.梳理问题的一定要有正确的逻辑,不可胡乱看到报错就去百度,要先思考。