最近公司的项目即将完成,正要进行最后的回归测试,正式release的时间线也快到了,比较紧张。结果就在这种关键时刻,在周五下午(恩,快下班的时候,我最恨这个时间出错了),发生一个bug:这个bug发生的几率极低,而一旦发生这个系统的traffic 都会因为这个bug报错退出。由于发生在关键时刻,于是被极度重视,当场召集人员开会,决定一定要第一时间查找出原因并尽快解决。
能够让系统的每个traffic请求都失败的bug,这个严重程度不言而喻。好吧,谁让我们是程序员呢,看看bug是怎么回事吧?
其实问题的表现很简单:在请求处理过程中,抛出了一个ThreadDeath 的error:
Caused by: java.lang.ThreadDeath
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1413)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
at se.ericsson.nrg.ws.realms.nrgcommonentities.service._ApplicationServiceSubscriptionRemote_DynamicStub.searchBySpIdAndAppIdAndScPk
(se/ericsson/nrg/ws/realms/nrgcommonentities/service/_ApplicationSe
rviceSubscriptionRemote_DynamicStub.java)
at se.ericsson.nrg.ws.realms.nrgcommonentities.service.ApplicationServiceSubscriptionDAORemoteImpl.searchBySpIdAndAppIdAndScPk(ApplicationServiceSubscriptionDAORemoteImpl.java:104)
... 74 more
坦白说,每次看到ThreadDeath这个error,我都有种一头撞死的感觉, ^0^,谁取的这名字!由于之前遇到过类似的问题,因此倒是不特别紧张,先看看请求处理的过程:首先是一个webapp接收请求,然后请求被交给一个 EJB的模块处理,ThreadDeath error就是在这里抛出。从ThreadDeath的exception trace上看到,
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1413)
说明当前线程是试图用webapp的classloader来装载类,然后遭遇ThreadDeath,随即在日志文件中查找到这样的内容:
[#|2010-05-24T15:19:26.702+0800|INFO|sun-glassfish-comms-server2.0|org.apache.catalina.loader.WebappClassLoader|_ThreadID=47;_ThreadName=httpWorkerThread-48080WorkerThread-8080-337;|PWC1635: Illegal access: this web application instance has been stopped already (the eventual following stack trace is caused by an error thrown for debugging purposes as well as to attempt to
terminate the thread which caused the illegal access, and has no functional impact)|#]
"this web application instance has been stopped already",进一步证实了我们的猜测,这个是最常见的ThreadDeath 的发生场景了,简单描述一下:
1. 工作线程执行webapp的请求,webapp的classloader被保存在thread
类似Thread.setContextClassLoader(WebappClassLoader)这样的方式
2. webapp被restart/redeploy ,但是由于某些原因上面的工作线程中保留的webapp的classloader并没有被清理
3. 这个工作线程继续处理请求,由于他使用的classloader是restart/redeploy前的webapp的,现在这个webapp已经不复存在,因此出现ThreadDeath 错误
有关ThreadDeath的类似错误,glassfish上有个wiki特地描述了这个问题。
http://wiki.glassfish.java.net/Wiki.jsp?page=FaqWebAppStoppedIllegalAccessError
正在我们以为找到问题准备继续查找看是哪里的Thread.setContextClassLoader()方法出现问题时,被另外一个发现击倒!我们发现上述的ThreadDeath 在重新启动之后居然可以立即重现!很晕,按照上面的推断逻辑,如果我们关闭glassfish,即关闭jvm,退出进程,然后重启启动,ThreadDeath 就应该消失才是,因为出问题的Thread随jvm一起退出了。新jvm中启动的Thread不再有残余的classloader来导致 ThreadDeath。
这个证据直接推翻前面的推断,看来问题不是这个简单了。
随后进行了大量的查找,推断和尝试,过程不叙述了。最后发现在日志中有一个异常,坦白说我们的日志有点多,有点乱,不是很好查找问题:
javax.servlet.ServletException: java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.Integer
at se.ericsson.nrg.ws.service.http.SIGAuthenticationFilter.init(SIGAuthenticationFilter.java:162)
at org.apache.catalina.core.ApplicationFilterConfig.getFilter(ApplicationFilterConfig.java:273)
at org.apache.catalina.core.ApplicationFilterConfig.setFilterDef(ApplicationFilterConfig.java:385)
at org.apache.catalina.core.ApplicationFilterConfig.<init>(ApplicationFilterConfig.java:119)
at org.apache.catalina.core.StandardContext.filterStart(StandardContext.java:4521)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:5369)
at com.sun.enterprise.web.WebModule.start(WebModule.java:345)
at com.sun.enterprise.web.LifecycleStarter.doRun(LifecycleStarter.java:58)
at com.sun.appserv.management.util.misc.RunnableBase.runSync(RunnableBase.java:304)
at com.sun.appserv.management.util.misc.RunnableBase.run(RunnableBase.java:341)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:619)
webapp中定义有一个filter,现在这个filter在init()方法中出现异常,而我们编码的时候会包装这个异常然后以 ServletException的方式抛给容器。
init()方法中出现的异常是一个配置项的类型不正确,做强制类型转换时失败导致。由于出现这个配置问题的脚本偶尔才运行,因此过去这个错误发生的几率极低。随即修复了这个配置项,重启glassfish后验证成功,ThreadDeath 错误消失,系统工作正常。
OK,问题找到并得到确认。
可是这里依然有一个巨大的疑问:为什么一个filter抛出的ServletException,会导致glassfish出现"判断失误"从而认定 webapp是"stopped"状态以致最后以ThreadDeath的形式来体现错误?
这个案例比较迷惑人的地方是,filter抛出的ServletException后,这个webapp是可以正常接收请求也可以正常将请求转交后面的业务处理模块,控制台上也看不到这个webapp的任何状态异常。
特地找了一下j2ee5规范,没有发现有对filter的详细要求,随即找到了j2ee 5的 API,在API文档中发现以下内容:
void init(FilterConfig filterConfig)
throws ServletException
Called by the web container to indicate to a filter that it is being placed into service. The servlet container calls the init method exactly once after instantiating the filter. The init method
must complete successfully before the filter is asked to do any filtering work.
The web container cannot place the filter into service if the init method either
1.Throws a ServletException
2.Does not return within a time period defined by the web container
这里可以看到,当发生ServletException 异常时,“The web container cannot place the filter into service”,既这个filter应该不能生效才是。而且也没有任何其他说明说ServletException会导致其他问题比如webapp不能启动,不能工作之类的。
当时实际上,我们在检查ThreadDeath的调用信息时,发现有下面的调用
at se.ericsson.nrg.ws.service.http.SIGAuthenticationFilter.doFilter(SIGAuthenticationFilter.java:89)
说明这个出现init()错误的filter还是被glassfish正常调用去执行doFilter()方法,这里和j2ee API的要求是不符合的。有点奇怪的是,glassfish一向是以严格遵循j2ee规范而著称,居然在这里一反常态。
而更令人 郁闷的是,glassfish在处理这个有filter初始化出现ServletException异常的webapp时的前后表现:首先这个 webapp的启动没有问题,状态正常。filter也被认为可以正常工作并加入了filter链。webapp中的功能正常,可以正常的接收请求并转发给内容业务处理模块。从这些迹象看这个webapp基本没有问题。但是后面glassfish却莫名其妙的认定,“this web application instance has been stopped already”,从而以ThreadDeath这种非常规的error来报错。
这个做法令人比较难于接收,如果filter初始化出现ServletException异常会导致webapp的状体异常,那么glassfish应该在第一时间直接给出这个判断,比如直接让webapp就启动失败之类的,这样不至于将这个错误推迟到一个非常遥远的地方才被暴露,而且filter ServletException异常 -》 ThreadDeath 的这种因果关系未免有点牵强,查错时根本没有可能直接联系上,导致查错的难度大增。
不得不再次批评一下glassfish,从产品质量上看,glassfish和weblogic的差距还真是不小。
分享到:
相关推荐
比如VirtualMachineError虚拟机错误,ThreadDeath线程僵尸等. 二.RuntimeException类及其子类表示的是非受检查异常,是系统可能会抛出的异常,程序员可以去处理,也可以不去处理,最经典的就是NullPointerException空...
| +-- java.lang.ThreadDeath | +-- java.lang.Exception | +-- java.lang.RuntimeException ``` `Throwable`是所有异常和错误的基类,包括`Error`和`Exception`。`Error`通常表示系统级别的错误,如JVM错误,...
而`Error`则表示更为严重的错误,通常是程序无法恢复的,比如`LinkageError`或`ThreadDeath`,通常由Java虚拟机直接抛出,不需要用户程序处理。 异常处理的基本结构包括`try-catch-finally`语句。`try`块包含可能会...
* Error 则表示出现了一个非常严重的异常错误,并且这个错误可能是应用程序所不能恢复的,例如 LinkageError 或 ThreadDeath 等。由 Java 虚拟机生成并抛出,Java 程序不做处理。 三、异常处理 2.1 异常处理的基本...
Error是一种严重的错误,是程序无法处理的异常,例如内存溢出(OutOfMemoryError)和线程死亡(ThreadDeath)等。Exception是程序本身可以处理的异常,分为运行时异常(RuntimeException)和非运行时异常。运行时异常是...
例如,`VirtualMachineError`(如`OutOfMemoryError`)表明系统内存不足,或者`ThreadDeath`表示线程被强制终止。这些错误通常是系统级别的,不是由程序员的错误导致的,而是由于系统资源耗尽或其他不可预见的情况。...
`Error`通常表示系统级别的错误,如虚拟机错误(Virtual Machine Error)、线程死亡(ThreadDeath)等,这些错误通常不期望程序员去处理,因为它们代表了系统的严重问题。而`Exception`是程序中可以预见的异常,包括...
其次,`Thread.stop` 可能触发 `ThreadDeath` 异常,这个异常是受检查的,但通常不会被捕获或处理。如果线程中存在未捕获的异常处理器,`Thread.stop` 的行为可能是不可预测的,可能会导致程序崩溃或者产生其他未...
7. **异常处理与线程**:介绍了线程中的异常处理,包括如何处理未捕获的`ThreadDeath`异常,以及线程间如何协作处理异常。 8. **线程安全编程**:讲述了如何编写线程安全的代码,包括使用不可变对象、最小化锁的...
1. **Error**:系统级错误,如虚拟机错误(VirtualMachineError)、线程中断(ThreadDeath)等。这些错误通常是由系统环境问题或硬件故障导致,程序员难以预防和恢复,因此不应捕获或处理。 2. **Exception**:应用...
Error 是程序无法处理的错误,比如 OutOfMemoryError、ThreadDeath 等。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。Exception 是程序本身可以处理的异常,这种异常分两大类运行时异常和非运行时异常。 ...
| +-- java.lang.ThreadDeath | +-- ... | +-- java.lang.Exception | +-- java.lang.RuntimeException | | | +-- java.lang.NullPointerException | +-- java.lang.ClassCastException | +-- ... | +--...
- `Error` 是程序无法处理的错误,如 `OutOfMemoryError` 或 `ThreadDeath`,会导致 JVM 终止线程。 - `Exception` 可以被程序处理,包括运行时异常(如 `NullPointerException`, `ArrayIndexOutOfBoundsException...
`Error`通常表示系统级别的错误,如`OutOfMemoryError`或`ThreadDeath`,这些错误往往超出程序员的控制范围,Java虚拟机(JVM)可能会在遇到这些错误时选择终止线程。 `Exception`是程序可以尝试处理的异常,分为两...
`Error`通常代表系统级或环境级别的问题,比如`OutOfMemoryError`和`ThreadDeath`,这些错误往往是程序员无法直接处理的,通常会导致JVM的终止。而`Exception`类则表示程序中可能出现的可预见的异常,可以被程序捕获...
1. **Error**:表示系统级错误或程序无法处理的错误,例如,虚拟机错误(VirtualMachineError)、线程死亡(ThreadDeath)等。 2. **Exception**:表示可以通过代码预防或处理的异常情况,分为运行时异常...
例如AbstractMethodError、AssertionError、ClassCircularityError、...ThreadDeath、ThreadStop、UnknownError、UnsatisfiedLinkError、UnsupportedClassVersionError、VerifyError、VirtualMachineError等...
使用`Thread.stop()`是不安全的,因为它可能导致数据不一致性和`ThreadDeath`异常的意外抛出,同时会立即释放线程持有的所有锁,引发问题。 6. **重入锁与条件对象**: `java.util.concurrent.locks....
- **错误**(Error):属于`java.lang.Error`的子类,如`OutOfMemoryError`、`ThreadDeath`等。错误通常是系统级别的问题,比如内存不足或虚拟机错误,一般情况下程序员无法完全避免或恢复。 4. **异常处理语法**...
java.lang.ThreadDeath 是一种线程结束,指的是在程序中调用 Thread 类的 stop 方法时抛出的异常。 30. java.lang.UnknownError 未知错误 java.lang.UnknownError 是一种未知错误,指的是在程序中 Java 虚拟机发生...