tomcat配置问题导致的OOM、频繁FullGC问题排查

本文详细解析了在生产环境中遇到的Tomcat服务频繁Full GC问题,通过Prometheus监控发现老年代内存异常,进一步分析发现是由于max-http-header-size配置不合理导致的大量byte[]数组占用内存。文章分享了如何定位问题根源,包括查看GC根持有引用,以及通过修改配置解决内存占用过高的过程。

生产某台服务出现频繁fullgc,一看Prometheus监控,老年代内存一看就很有问题,突然陡峭的曲线,就像房价一样。
在这里插入图片描述
导出dump文件到本地查看,有个byte对象都是10M大小,占了内存的80%以上,简单一看是http请求的信息,事情似乎有点眉目。
在这里插入图片描述
再看GC根谁持有了byte[]数组的引用。
在这里插入图片描述
这符合之前的猜测,是tomcat的线程在处理过程中分配了10M的buffer在堆上。至此,马上可以想到一定是什么参数设置的不合理导致了这种情况,一般而言tomcat不可能为每一个请求分配如此大的buffer。

检查代码里是否有tomcat或服务器相关配置,看到有这么一个配置:
max-http-header-size: 10000000
至此,基本已经确定就是这个不合理的最大http请求头参数导致的问题。

可以打开线程视图,搜索一下tomcat的工作线程,发现线程数量的确很多有401个,但是也只是800的一半。
在这里插入图片描述
另外,发现除了有10008192字节的数组还有10000000字节的数组,查看引用路径可以看到这个正好是10M的数组是output buffer,区别于之前看到的input buffer
在这里插入图片描述
本地验证:
本地启动用visualVM监控,jmeter压测了一波,重现了这个陡峭的曲线。
在这里插入图片描述

修改配置max-http-header-size:4096后,曲线变得正常。
在这里插入图片描述

<think>我们正在排查Java服务崩溃是否由数据库问题导致。数据库问题可能包括连接池耗尽、慢查询导致线程阻塞、数据库连接超时等。以下将结合引用内容,分步骤说明排查方法: 引用[1][2][3]提到服务崩溃可能与OOM(内存溢出)或Linux OOM-Killer有关,但用户现在关注的是数据库因素。因此,我们需要从数据库角度切入,同时注意内存问题也可能是数据库操作引起(如大结果集导致内存溢出)。 排查步骤: 1. **检查Java服务日志**: 首先查看Java服务的日志(如catalina.out、应用日志文件),寻找与数据库相关的错误信息,例如: - 连接超时:`java.net.ConnectException: Connection timed out` - 连接池耗尽:`Cannot get a connection, pool error Timeout waiting for idle object` - 数据库操作超时:`SQLTimeoutException` - 数据库连接断开:`Communications link failure` 如果发现此类错误,说明崩溃可能与数据库连接问题有关[^1]。 2. **监控数据库连接池**: 如果应用使用数据库连接池(如HikariCP、Tomcat JDBC Pool),检查连接池配置和运行状态: - 连接池最大连接数是否过小? - 是否有连接泄漏(即连接未关闭)? 可以在应用启动时开启连接池监控,记录连接池状态(如活跃连接数、等待连接的线程数)。如果等待线程数激增,可能因数据库响应慢导致线程堆积,最终耗尽资源(如线程池、内存)引发崩溃。 3. **分析数据库性能**: 数据库性能问题(如慢查询、锁竞争)可能导致Java服务线程阻塞,进而引发资源耗尽(如线程池满、连接池耗尽)。排查方法: - 在数据库端执行慢查询分析(MySQL可使用`SHOW FULL PROCESSLIST`或开启慢查询日志)。 - 检查数据库服务器的CPU、内存、磁盘I/O是否过载(使用`top`、`iostat`等命令)。 - 若数据库响应延迟高,Java服务可能出现大量线程等待,最终导致服务崩溃。 4. **检查线程堆栈**: 在服务崩溃前,可通过线程转储(thread dump)分析线程状态: - 使用`jstack <pid>`命令获取线程快照。 - 查找大量线程阻塞在数据库操作(如`java.net.SocketInputStream.socketRead0`)或等待数据库连接。 如果崩溃后无法获取现场,可配置JVM在崩溃时自动生成线程转储(添加JVM参数`-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/dump`)[^1][^3]。 5. **检查系统资源**: 数据库问题也可能间接导致系统资源耗尽,触发OOM-Killer: - 若数据库响应慢,Java服务线程堆积,可能占用大量内存(每个线程的栈内存和请求上下文),最终导致系统内存不足,被OOM-Killer终止[^3]。 - 使用`free -h`检查内存使用,使用`dmesg | grep -i kill`查看OOM-Killer日志(若有记录则说明进程因内存不足被杀)[^2][^3]。 6. **模拟压力测试**: 使用工具(如JMeter)模拟数据库请求,观察在高压下是否重现崩溃,并监控: - 数据库连接池状态 - 线程池状态 - 内存变化 通过压力测试可确认数据库问题与崩溃的因果关系。 7. **网络问题排查**: 数据库与Java服务间的网络不稳定可能导致连接中断。检查: - 网络延迟和丢包率(`ping`、`traceroute`) - 防火墙或安全组设置(是否中断长连接?) 总结:若数据库连接或性能问题导致Java服务崩溃,通常会在日志中留下直接证据(如连接超时、连接池耗尽)或间接证据(如线程堆栈显示大量数据库操作阻塞)。同时,需注意数据库问题可能引发连锁反应(如内存溢出或线程资源耗尽),最终被OOM-Killer终止。因此,需结合日志、资源监控和线程分析综合判断。 如果以上步骤未发现数据库问题,则需回归基础排查(如JVM内存设置、系统资源等),参考引用[1][2][3]中提到的OOM-Killer和内存溢出分析。 §§相关问题§§ 1. 如何配置数据库连接池以避免连接耗尽? 2. 如何监控和分析MySQL慢查询? 3. Java服务中数据库连接泄漏的常见原因和排查方法? 4. Linux下如何诊断网络问题导致的数据库连接中断?</think>### 排查Java服务崩溃是否由数据库问题导致的方法 数据库连接或性能问题可能导致Java服务崩溃,以下是系统化的排查步骤: --- #### 1. **检查Java服务日志中的数据库错误** - **关键操作**: - 查看服务日志(如 `catalina.out` 或应用日志文件),搜索以下关键词: ```bash grep -Ei "SQLException|ConnectionTimeout|PoolExhausted|Communications link failure" /path/to/app.log ``` - 重点关注: - 连接超时(`Connection timed out`) - 连接池耗尽(`Cannot get a connection from pool`) - 查询超时(`Query timeout`) - **典型问题**: - 数据库连接池耗尽导致线程阻塞,最终触发OOM崩溃[^1] - 慢查询导致线程堆积,消耗所有内存后被OOM-Killer终止[^3] --- #### 2. **监控数据库连接池状态** - **排查工具**: - 使用JDBC连接池监控(如HikariCP的`HikariPoolMXBean`): ```java // 代码示例:输出连接池状态 HikariPoolMXBean poolBean = hikariDataSource.getHikariPoolMXBean(); System.out.println("Active Connections: " + poolBean.getActiveConnections()); System.out.println("Idle Connections: " + poolBean.getIdleConnections()); ``` - **关键指标**: - `ActiveConnections` 持续接近最大连接数 - `ThreadsAwaitingConnection` > 0 表示线程在等待连接 - **风险点**: - 连接泄漏会导致连接池耗尽,最终服务崩溃 --- #### 3. **分析数据库性能** - **数据库慢查询检查**: - MySQL执行慢查询分析: ```sql SHOW FULL PROCESSLIST; -- 查看当前运行查询 SELECT * FROM performance_schema.events_statements_summary_by_digest ORDER BY SUM_TIMER_WAIT DESC LIMIT 10; -- 最耗时的SQL ``` - **外部工具**: - 使用 `pt-query-digest` 分析MySQL慢日志 - 用 `EXPLAIN` 分析SQL执行计划 - **关联崩溃**: - 慢查询导致Java线程阻塞,堆积的线程可能引发内存溢出[^1] --- #### 4. **检查网络与连接状态** - **网络诊断**: - 测试数据库端口连通性: ```bash telnet <db_host> 3306 # MySQL默认端口 ``` - 抓包分析(需sudo权限): ```bash tcpdump -i eth0 port 3306 -w db_capture.pcap ``` - **关键问题**: - 网络闪断导致连接中断(日志中出现 `Communications link failure`) - 防火墙阻断数据库端口 --- #### 5. **验证资源限制** - **系统级检查**: - 查看进程打开文件数限制: ```bash cat /proc/$(pgrep -f java)/limits | grep 'open files' ``` - 检查数据库最大连接数配置(MySQL示例): ```sql SHOW VARIABLES LIKE 'max_connections'; ``` - **崩溃关联**: - 文件描述符耗尽会导致新建数据库连接失败 - 超过数据库最大连接数会导致Java服务获取连接失败 --- #### 6. **压力测试与监控** - **模拟验证**: - 使用JMeter模拟数据库请求高峰 - 监控期间关注: - Java堆内存:`jstat -gc <pid> 1000` - 线程状态:`jstack <pid> | grep -c 'RUNNABLE'` - **崩溃复现**: - 当数据库响应延迟 > 连接超时时间时,可能触发雪崩崩溃 --- ### 总结排查路径 ```mermaid graph TD A[服务崩溃] --> B{检查Java日志} B -->|发现DB错误| C[数据库问题导致] B -->|无DB错误| D{监控连接池} D -->|连接池耗尽| E[检查连接泄漏/慢SQL] D -->|正常| F{系统资源检查} F -->|文件描述符不足| G[调整ulimit] F -->|OOM-Killer日志| H[内存优化] ``` 当确认数据库问题后,解决方案包括: 1. 优化慢查询,添加数据库索引 2. 增大连接池配置(如 `maxLifetime` 和 `maximumPoolSize`) 3. 实现连接重试机制(需配合超时设置) 4. 增加数据库资源或读写分离
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值