什么情况下tomcat服务器会挂,tomcat服务器频繁宕机怎么解决
- 云服务器
- 2026-08-27
- 2
Tomcat服务器会挂,根本原因是内存溢出、线程阻塞或连接数耗尽,其中JVM内存配置不当和代码层面的资源泄漏是绝大多数宕机的幕后推手。
在我处理过的各种tomcat故障里,真正被瞬时流量打崩的其实只占一小部分,更多时候是慢性病:日志越堆越多、内存慢慢被吃光、连接池里的连接排着队超时,你以为它是突然死的,其实它已经难受了好几天,下面我把常见的死法按出现频率从高到低拆开讲,顺带说清楚每种情况该怎么判断、怎么救。
Tomcat卡死最常见的原因:JVM内存配置与实际负载不匹配
Tomcat跑在JVM上,JVM的内存管不好,tomcat说崩就崩,最常见的是堆内存设置太小,默认的-Xms和-Xmx在稍微有点流量的环境下根本不够用,另一个极端是堆内存设得太大但机器物理内存有限,操作系统开始用swap交换分区,性能断崖式下跌,看起来就是tomcat卡住不动了。
典型症状是:
- 服务还能ping通,但页面转圈很久才响应
- 日志里频繁出现OutOfMemoryError: Java heap space
- 或者出现GC overhead limit exceeded
遇到这种情况,先在catalina.sh(Linux/Mac)或catalina.bat(Windows)里检查JVM参数:
JAVA_OPTS="-Xms2048m -Xmx2048m -XX:MaxMetaspaceSize=512m"
行业共识认为,-Xms和-Xmx应该设为相同值,避免运行期动态扩容引发Full GC,如果你是4核8G的机器,建议堆内存给到3G左右,留出足够空间给操作系统和线程栈,如果设置了合理参数后仍然频繁Full GC,那就得用jstat -gcutil <pid> 1000观察垃圾回收频率,配合jmap -dump:format=b,file=heap.hprof <pid>导出堆快照来分析。
内存泄漏:重启后恢复正常,过一段时间又慢下来
这是最让人头疼的一种情况,tomcat刚启动时一切正常,跑个几天几十天,响应越来越慢,最后直接挂掉,问题通常出在代码里:
- 数据库连接没用完不归还,连接池被打满
- 大文件上传下载后流没有关闭
- 静态变量或缓存里存了太多数据没有清理机制
- ThreadLocal使用后没有remove
排查思路是:tomcat挂之前先抓堆快照,用MAT(Memory Analyzer Toolkit)分析支配树,看哪个对象占了绝大部分内存,顺着引用链就能找到是哪段代码的问题,如果是连接泄漏,可以监控连接池的active数量和idle数量,active长时间占满基本可以确定是连接没释放。
连接数被耗尽:tomcat服务器线程池被打满的表现与处理
连接数打满也算得上是tomcat宕机的高发原因之一,前面提到的是内存层面,这里说的是并发层面,tomcat默认的maxThreads是200,也就是说同一时刻最多只能处理200个请求,如果请求处理得慢(比如某个接口查了10秒数据库),200个线程全部被占住,后面的请求就只能排队,排队的直接表现就是请求超时。
tomcat被连接数压垮时,访问日志里能看到什么:
- 浏览器端显示502或504
- catalina.out里出现Connection timed out或All threads are busy相关记录
- 请求耗时极长,但CPU占用率不高
这里面有个容易混淆的问题:tomcat挂了和tomcat服务器卡顿的区别,连接池满的时候tomcat进程其实还活着,端口也能通,但业务上已经完全不可用,这种情况下,调大maxThreads有用但效果有限,因为线程多了CPU上下文切换成本也上去了,更重要的是要找出哪个接口拖慢了整体响应,一般用jstack抓线程栈,看看线程都卡在什么方法上。
jstack <pid> > thread_dump.txt
抓几次线程栈,对比线程状态,如果大量线程卡在同一个数据库操作上,那问题大概率在SQL或数据库连接池配置上,行业共识认为,先优化慢查询,再考虑调大线程数,顺序反了就是花钱买延迟。
数据库连接池满导致tomcat假死
这其实不算tomcat本身的故障,但现象一模一样,tomcat的线程在等待数据库连接时全部进入WAITING状态,表现就是tomcat挂着不动,排查时可以查一下Druid或HikariCP连接池的监控页面,看active连接数和连接等待时间,如果连接池大小是20,而某个业务高峰时段的并发查询超过了这个数,就会开始排队,一个连接执行一个慢查询耗时5秒,20个连接就只能支撑每秒4个这类请求,后面的全部阻塞。
外部依赖拖垮tomcat:磁盘写满与系统资源耗尽
tomcat服务器挂了不一定是tomcat自己的问题,系统层面的资源耗尽同样致命,最常见的是磁盘空间满,tomcat的日志还在不断写入,此时catalina.out写入失败,服务直接异常退出或卡死,日志文件是默认的catalina.out,如果没做日志切割,大流量跑一个月轻松上几十GB。
检查磁盘空间:
df -h
占用超过80%就得注意了。日志切割策略至少要做到按天切,同时清理N天前的历史日志,应用日志同样需要关注,特别是框架里打印的debug日志,在高并发下产生的量非常惊人。
系统层面的其他问题包括文件描述符耗尽,Linux默认的文件描述符限制是1024,tomcat作为服务器需要打开大量socket连接和文件,这个值远远不够,修改/etc/security/limits.conf并重新登录后生效:
soft nofile 65536 hard nofile 65536
顺带提一个对比场景:很多人会纠结tomcat和nginx谁更容易挂,实际上两者定位不同,nginx作为反向代理在前面挡静态资源和并发连接,tomcat专注处理动态请求,tomcat服务器性能提升的有效手段之一就是在前面架一台nginx,nginx处理高并发的能力远强于tomcat的HTTP线程池,能帮你挡掉很多不必要的连接开销。
突然的大流量冲击:瞬秒与爬虫场景的应对
瞬时高并发也是一种常见死法,比如活动上线那一秒几十万请求打进来,tomcat根本来不及响应,这类场景下,光调tomcat参数是救不了的,必须在前端层面做限流和排队,用nginx的limit_req模块做接口级限流,或者在应用层加一个简单的令牌桶,还有一种是恶意爬虫反复抓取页面,占比不小,它们不遵守robots协议,每个IP开几十个连接,直接吃掉你所有线程,用防火墙或nginx按IP做并发限制能立竿见影。
Tomcat配置层面的隐藏坑:线程池与连接器参数
server.xml里的Connector参数很多人只改了个端口就上线了,里面其实藏着不少坑。
| 参数 | 默认值 | 建议调整方向 |
|---|---|---|
| maxThreads | 200 | 根据CPU核心数和业务耗时调整 |
| minSpareThreads | 25 | 维持核心可用线程数 |
| acceptCount | 100 | 等待队列长度,不宜过大 |
| connectionTimeout | 20000ms | 按业务场景缩短 |
| maxConnections | 8192 | NIO模式下该值参考系统fd限制 |
核心思路是让线程数匹配业务耗时,如果接口平均耗时100ms,200个线程每秒能处理2000个请求,这是健康的节奏,如果接口平均耗时2秒,200个线程每秒只能处理100个请求,稍有波动就堆积。
连接器的IO模型选择
Tomcat 8.5+推荐使用NIO,Tomcat 9开始默认NIO,如果你的tomcat还在用BIO模型(Tomcat 8.0及早期版本),在高并发场景下每个连接占一个线程,性能和稳定性都会差很多,检查一下server.xml里Connector的protocol属性:
<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol" ... />
Http11NioProtocol就是NIO模型,如果是Http11Protocol则说明还在用BIO,建议升级或改配置。
出现OOM或其他致命错误时,tomcat服务器会自动重启吗
默认情况下不会,没有配置-XX:OnOutOfMemoryError时,tomcat在堆内存耗尽后会持续Full GC,这个过程看起来像死循环,端口还在,但请求完全无响应,如果你用的是systemd管理服务,配了Restart=on-failure,那OOM导致JVM退出后系统会自动拉起,但从外部看就是tomcat发生了中断重启,在生产环境里,部署监控和告警远比依赖自动重启更靠谱,推荐用jstat定时记录GC情况,或者接Prometheus + Grafana监控堆内存使用趋势,在内存涨到危险水位之前提前介入。
常见疑问和应急处理
Tomcat服务器卡顿和tomcat挂掉的本质区别是什么?
卡顿通常指服务还能响应,但耗时明显变长;挂掉则完全不可用,卡顿往往是内存增长或线程池排队的前期信号,从卡顿到挂掉一般有一个过程,抓住这个过程做排查,比等彻底挂了再抓堆快照要容易得多,如果连不上了,先确认进程是否还在ps -ef | grep tomcat,如果进程退出就是JVM崩溃,如果进程在但响应不了,多半是堆内存被打满或线程死锁。
重启一下又正常,之后还挂怎么办?
重启能解决的是状态型问题内存里的脏数据被清干净、连接池重新初始化,但如果是配置不合理或代码有泄漏,重启只是把时间线往后推,正确做法是重启之前先收集现场信息:抓heap dump、抓线程栈、记录当时的GC日志、看系统负载,这些数据拿到手再重启,否则下一次挂掉仍然毫无头绪。
Tomcat的默认堆内存配置值为什么容易出问题?
同样配置情况下,不同机器上tomcat的表现差异可能很大,默认参数的目标是兼容大多数低配置机器,所以设计得非常保守,现在服务器普遍有多个核心和大量内存,默认的-Xmx(通常只有256MB左右)根本发挥不出硬件性能,这也是为什么很多人在自己电脑上跑得好好的,一部署到服务器上就频繁宕机不是代码变了,是资源水位变了。
Tomcat挂掉的原因再多,落到根上就三句话:内存管不住、线程不够用、外部依赖拖后腿,日常运维时,给JVM参数做一次体检,给访问日志配上切割,给关键接口加个超时控制,再把GC和堆内存的监控搭起来,绝大多数tomcat崩溃都能提前拦截,这不是高深的技术,只是把该做的防护做在了前面。