Tomcat随机崩溃报safepoint.cpp:687死锁错误求助
Tomcat随机崩溃(Safepoint死锁)排查与解决指南
一、先从近期配置变更入手排查
报错信息:
Internal Error (safepoint.cpp:687), pid: xxx, tid: xxx
具体描述:Deadlock in safepoint code. Should have called back to the VM before blocking
回滚配置验证
一周前的server.xml变更(端口调整、密钥库更新、shutdown指令修改)是最高优先级排查点:- 临时恢复shutdown指令为原默认值
- 把80/8080端口Connector的
redirectPort改回443,443端口Connector恢复原配置,AJP端口改回8009 - 换回旧密钥库文件
观察回滚后是否还会崩溃,若恢复正常,再逐一重新应用变更,定位具体触发项。
检查新密钥库兼容性
新密钥库可能存在格式、算法或证书链问题,导致SSL线程异常阻塞:- 用
keytool -list -v -keystore <新密钥库路径>查看密钥库信息,确认密钥算法(如RSA)、证书有效期、信任链是否完整 - 对比旧密钥库配置,确保新密钥库使用的算法是JDK 8u181原生支持的(避免用JDK8后期才兼容的算法)
- 用
二、VM层面定位Safepoint死锁
分析hs_err文件
从两个hs_err文件中重点提取:- 崩溃时处于阻塞状态的线程,尤其是
http-nio-8443-exec-*、ajp-nio-8004-*这类和新端口相关的线程 - 查看
safepoint state部分,确认未到达安全点的线程及其调用栈 - 排查是否有线程在执行JNI调用、Socket I/O阻塞时未正确释放VM锁
- 崩溃时处于阻塞状态的线程,尤其是
添加VM参数监控安全点行为
在Tomcat启动脚本中加入以下参数,收集更详细的安全点日志:-XX:+PrintSafepointStatistics -XX:PrintSafepointStatisticsCount=1 -XX:+PrintGCApplicationStoppedTime -XX:+PrintGCApplicationConcurrentTime日志会输出安全点触发原因、停顿时间、未响应线程信息,帮你定位死锁根源。
三、不升级JDK的可行解决方案
调整Tomcat线程池配置
若问题和SSL线程阻塞相关,调整Connector线程池参数降低阻塞概率:- 降低
maxThreads,避免过多线程同时执行SSL握手或I/O操作 - 增加
connectionTimeout,减少线程因超时等待引发的异常 - 启用
asyncTimeout,及时清理异步请求的阻塞线程
- 降低
切换到APR/OpenSSL实现
放弃JVM默认的JSSE,改用Tomcat APR绑定的OpenSSL,将SSL处理移到Native层,减少JVM层面的安全点冲突:- 安装APR库和Tomcat Native组件
- 在SSL Connector中配置
protocol="org.apache.coyote.http11.Http11AprProtocol"
更换补丁版同版本JDK
找第三方维护的JDK 8u181发行版(如Adoptium Temurin),这类版本通常会backport官方后期修复的安全点死锁补丁,无需升级大版本,避免兼容性问题。
四、其他排查方向
- 检查系统资源:CPU、内存、文件句柄耗尽可能导致线程阻塞,进而触发安全点死锁
- 排查JNI调用:如果应用用了JNI库,确认JNI代码是否规范,避免在JNI中长时间阻塞而未回调VM
内容的提问来源于stack exchange,提问作者Babar Ali
相关产品推荐
相关产品推荐

