嵌入式Tomcat会卡死,直到我点击Eclipse窗口才会恢复
我在一个使用Eclipse 4.12.0、带嵌入式Tomcat的项目中工作。
在测试一个通过AJAX与后端通信的页面时,整个服务有时似乎会冻结。
具体表现如下:
- 网页上的按钮点击后没有任何反应。
- 在Chrome和 Firefox的开发者工具中,请求要么根本没有发送,要么已发送但始终收不到响应。
- 两个浏览器都出现同样的问题,因此我认为这与浏览器无关。
- 但当我单击Eclipse窗口时,所有延迟/待处理的工作突然被处理,系统再次开始正常工作。
- 之后如果再次测试网页,问题可能再次出现。再次点击Eclipse让它恢复。
- 重启Eclipse(包括嵌入式Tomcat)暂时解决了问题。
看起来像是Eclipse或嵌入式Tomcat的某些东西被阻塞、暂停或卡住,直到Eclipse重新获得焦点。
有人有过类似的经历吗?
我不确定应该先排查什么:内存泄漏、被阻塞的Tomcat线程、Eclipse调试器的行为、UI/焦点相关问题,还是其他原因。
尤其担心的是,这是否也可能在面向客户的生产环境中发生,在那种环境下整 个服务无响应将是一个严重问题。
需要排查的事项
几个需要调查的点:
Sysout/Syserr阻塞
写入 System.out 可能会阻塞,我也能理解为什么切换焦点到Eclipse会解开这种阻塞:你的操作系统会冻结Eclipse进程,从而不再从 System.out 读取数据,一旦缓冲区满,继续向 System.out 写入就会阻塞,这就可能导致你所看到的现象。
要调查——下面的方法可能更简单。否则:自行实现一个 PrintStream,它获取时间戳(System.currentTimeMillis() / Instant.now()),调用“真实”的sysout,然后再次获取时间戳。如果两次时间戳的差值超过大约100ms,就输出一条额外的消息来指示这一点(System.out.println(">>!!! That took a long time: " + delta);)。然后对 System.err.println 及同类项执行同样的操作。
使用 System.setOut 和 System.setErr 将这些变体覆盖到标准输出和标准错误输出。然后运行你的应用,等待冻结,等上几秒,点击Eclipse。如果出现这些消息——你就有答案了。生产环境中也不太可能发生,但这确实有点代码味道。基于服务器的架构不应该向系统输出发送任何信息,除非你已经用 System.out:(此处保留原文占位符的风格)把你整套日志基础设施路由通过sysout/syserr。
被阻塞的线程
在应用程序正常运行、未被阻塞、基本处于空闲状态时,使用命令行上的 jstack 工具来获取所有线程的转储。
然后让应用处于这种被阻塞的状态,在你聚焦Eclipse以解除冻结之前,重新运行 jstack。
然后聚焦Eclipse以解除阻塞,一旦应用稳定,再次运行它进行第三次比较。
对比输出。第二次应该有一个或多个线程在某处被卡住,与第一次和最后一次的输出不同(或中间那次在此模式下的线程数应多于第一次/最后一次)。这些不同的轨迹就是你需要调查的线索。