已解决Abandoned连接清理线程泄漏,求助Mojarra线程内存泄漏问题
我看你已经搞定了「Abandoned connection cleanup thread」的线程泄漏问题,但现在卡在了Mojarra的WebResourceMonitor线程上——这个问题其实是Mojarra(JSF的官方实现)里的一个常见小坑,尤其是在Web应用重启或者停止的时候,这个监控线程没被正确销毁,进而引发内存泄漏。结合你给出的线程堆栈(sun.misc.Unsafe.park(Native Method)说明线程正处于等待状态,没被正常终止),给你几个实用的解决方案:
方案1:直接升级Mojarra版本
这个线程泄漏的问题在Mojarra的旧版本里是已知bug,比如2.2.x的部分版本、2.3.x的早期版本都存在这个问题。官方在后续的稳定版本里已经修复了线程销毁的逻辑,直接升级到最新的稳定版(比如2.3.14及以后,或是Jakarta Faces 3.x/4.x的对应版本),基本就能彻底解决这个问题,这也是最推荐的方案,毕竟升级还能顺便获得其他bug修复和性能提升。
方案2:手动写监听器终止线程(临时应急方案)
如果暂时没法升级版本,可以自己写一个ServletContextListener,在应用停止的时候主动找到这个线程并终止它:
import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import java.lang.reflect.Field; import java.util.Set; public class MojarraThreadCleanupListener implements ServletContextListener { @Override public void contextDestroyed(ServletContextEvent sce) { try { // 反射获取WebResourceMonitor的活跃线程集合 Class<?> monitorClass = Class.forName("com.sun.faces.application.resource.WebResourceMonitor"); Field threadSetField = monitorClass.getDeclaredField("activeThreads"); threadSetField.setAccessible(true); Set<Thread> activeThreads = (Set<Thread>) threadSetField.get(null); // 逐个中断线程并等待终止 for (Thread thread : activeThreads) { thread.interrupt(); try { thread.join(1000); // 等待1秒让线程完成收尾 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } catch (Exception e) { e.printStackTrace(); } } @Override public void contextInitialized(ServletContextEvent sce) { // 初始化阶段无需操作 } }
写完后,在web.xml里注册这个监听器:
<listener> <listener-class>com.yourpackage.MojarraThreadCleanupListener</listener-class> </listener>
⚠️ 注意:这个方案依赖Mojarra的内部类和字段,不同版本可能有结构变化,后续升级Mojarra后记得移除这个监听器,避免出现兼容问题。
方案3:关闭不必要的资源监控
如果你的项目不需要Mojarra的资源监控功能,可以直接在web.xml里添加参数关闭它,从根源上避免这个线程被启动:
<context-param> <param-name>com.sun.faces.enableResourceMonitoring</param-name> <param-value>false</param-value> </context-param>
关闭后,WebResourceMonitor就不会启动对应的监控线程,自然也就不会有泄漏问题了。
内容的提问来源于stack exchange,提问作者Newbie

