JAVA内存泄漏问题分析优化
根据最新提供的系统监控数据和进程信息,以下是深入分析和具体建议:
## 关键进程内存占用分析
### 内存消耗Top 3进程
1. **Tomcat Java应用 (PID 31037)**:
- 占用内存:5.26GB (32.5%)
- JVM配置:`-Xms2048m -Xmx4096m` (堆内存2-4GB)
- 实际RSS远超堆设置,可能存在:
* 直接内存泄漏(通过`-XX:MaxDirectMemorySize=1024m`配置)
* 大量JNI调用或本地内存分配
2. **MySQL (PID 1129)**:
- 占用内存:2.53GB (15.6%)
- 长期运行(88460小时),需检查:
```bash
mysqladmin variables | grep buffer_pool
mysqladmin status | grep -E 'Threads|Memory'
```
3. **shun-sync服务 (PID 5522)**:
- 占用内存:1.35GB (8.3%)
- JVM配置:`-Xms1024m -Xmx1024m` (堆设置合理)
## 内存问题诊断
### 异常现象确认
1. **物理内存**:
- 总内存:16GB
- 已用:15.97GB (99.8%)
- 空闲:186MB (1.2%)
- 缓存:2.3GB (偏低)
2. **交换空间**:
- 总量:4GB
- 使用量:1.3MB (0.03%)
- 缓存比例:10.5% (正常)
3. **内存分布**:
- Active内存:12.4GB (其中匿名页11.1GB)
- Inactive内存:2.8GB
## 根本原因分析
1. **Tomcat内存泄漏**:
- RSS(5.26GB) > Xmx(4GB) + MaxDirectMemory(1GB)
- 可能原因:
* JNI库内存泄漏
* 未受JVM管控的本地内存分配
* 线程栈过多(`-Xss`设置过大)
2. **swappiness配置不当**:
- 当前配置可能为0,导致系统宁愿触发OOM也不使用swap
- 验证命令:
```bash
cat /proc/sys/vm/swappiness
```
3. **MySQL内存配置**:
- 未优化的InnoDB缓冲池可能导致内存碎片
## 紧急处理方案
### 立即释放内存
```bash
# 清理页面缓存(临时方案)
sync; echo 1 > /proc/sys/vm/drop_caches
# 重启最耗内存的Tomcat(需安排维护窗口)
systemctl restart tomcat
```
### JVM内存诊断
```bash
# 安装JDK工具
apt-get install -y openjdk-8-jdk
# 检查Tomcat内存详情
jcmd 31037 VM.native_memory summary
jmap -histo:live 31037 | head -20
```
## 长期优化建议
1. **Tomcat优化**:
```diff
- -XX:MaxDirectMemorySize=1024m
+ -XX:MaxDirectMemorySize=512m
+ -XX:NativeMemoryTracking=summary
+ -XX:+AlwaysPreTouch
```
2. **MySQL优化**:
```ini
# /etc/mysql/my.cnf
[mysqld]
innodb_buffer_pool_size = 1G
innodb_buffer_pool_instances = 4
```
3. **系统参数调整**:
```bash
# 防止OOM killer误杀
echo 'vm.panic_on_oom=2' >> /etc/sysctl.conf
echo 'vm.overcommit_memory=2' >> /etc/sysctl.conf
sysctl -p
```
4. **监控增强**:
```bash
# 内存泄漏检测
apt-get install -y sysstat valgrind
sar -r 60 -o /var/log/sa/memlog &
```
## 验证指标
优化后应达到:
- 内存使用率 <90%
- Swap使用率 1-5%
- 缓存占比 >20%
- 关键进程RSS ≈ Xmx + 30%
根据 `jcmd` 和 `jmap` 的输出分析,Tomcat 进程 (PID 31037) 存在严重的内存管理问题。以下是详细诊断和解决方案:
---
## **1. 内存占用分析**
### **1.1 Native Memory 分布**
- **总 Reserved**: 6,165,979 KB (~6.1GB)
- **总 Committed**: 4,780,715 KB (~4.78GB)
| 内存区域 | Reserved (KB) | Committed (KB) | 问题点 |
|------------------|--------------|---------------|--------|
| Java Heap | 4,194,304 | 3,899,392 | 接近 Xmx 上限 (4GB) |
| Class Metadata | 1,280,500 | 260,596 | **MetaSpace 占用高** |
| Thread Stacks | 186,976 | 186,976 | **179 个线程,栈占用过大** |
| Code Cache | 273,364 | 203,492 | **JIT 编译代码缓存较大** |
| GC 相关 | 159,054 | 158,478 | G1GC 占用偏高 |
| Symbol Storage | 44,112 | 44,112 | 字符串常量池较大 |
### **1.2 Heap 内存分析 (`jmap -histo`)**
- **Top 内存占用对象**:
- `char[]` (`[C`): **379MB**
- `byte[]` (`[B`): **171MB**
- `String`: **13MB**
- `LinkedHashMap$Entry`: **9.4MB**
- `Object[]` (`[Ljava.lang.Object;`): **8.5MB**
**问题点**:
- **大量 `char[]` 和 `byte[]` 占用**,可能是:
- **未优化的字符串处理**(如日志、JSON 解析)
- **缓存未限制大小**(如 HTTP Session 缓存)
- **内存泄漏**(如静态集合未清理)
---
## **2. 问题诊断**
### **2.1 MetaSpace 占用高**
- **Class Metadata 占用 260MB**,加载了 **37,630 个类**。
- **可能原因**:
- **动态类加载**(如 JSP 编译、反射生成类)
- **未限制 MetaSpace**(默认无上限,可能无限增长)
### **2.2 线程栈占用过大**
- **179 个线程**,每个线程栈占用约 **1MB**(默认 `-Xss1m`)。
- **总栈内存**:179 × 1MB ≈ **179MB**。
- **可能原因**:
- **线程池配置过大**(如 Tomcat `maxThreads=200`)
- **线程泄漏**(如未正确关闭线程池)
### **2.3 Direct Memory 泄漏**
- `-XX:MaxDirectMemorySize=1024m` 设置了 **1GB 堆外内存**。
- **NIO Buffer 或 Netty 等框架可能泄漏 Direct Memory**。
---
## **3. 优化方案**
### **3.1 JVM 参数优化**
```diff
# 修改 Tomcat 启动参数(catalina.sh 或 setenv.sh)
- -Xms2048m -Xmx4096m -XX:MaxDirectMemorySize=1024m
+ -Xms2048m -Xmx4096m
+ -XX:MaxDirectMemorySize=512m # 限制 Direct Memory
+ -XX:MaxMetaspaceSize=256m # 避免 MetaSpace 无限增长
+ -Xss256k # 减少线程栈大小
+ -XX:+UseG1GC # 改用 G1 垃圾回收器
+ -XX:NativeMemoryTracking=detail # 持续监控 Native Memory
```
### **3.2 检查线程泄漏**
```bash
# 查看 Tomcat 线程数
ps -L -p 31037 | wc -l
# 查看线程栈
jstack 31037 > /tmp/jstack.log
grep "pool-" /tmp/jstack.log | wc -l
```
**优化建议**:
- 调整 `server.xml` 中的 `maxThreads`(如从 200 → 100)。
- 检查是否有自定义线程池未正确关闭。
### **3.3 检查 MetaSpace 泄漏**
```bash
# 查看加载的类数量
jcmd 31037 GC.class_stats | head -20
# 检查是否有动态生成的类
jcmd 31037 VM.class_hierarchy | grep Generated
```
**优化建议**:
- 限制 `-XX:MaxMetaspaceSize=256m`,防止无限增长。
- 检查是否有频繁的 JSP 热部署。
### **3.4 检查 Direct Memory 泄漏**
```bash
# 查看 NIO Buffer 分配
jcmd 31037 VM.native_memory detail | grep "Java Heap\|Direct"
```
**优化建议**:
- 如果使用 Netty,检查 `ByteBuf` 是否正确释放。
- 限制 `-XX:MaxDirectMemorySize=512m`。
---
## **4. 监控与验证**
### **4.1 监控内存变化**
```bash
# 每 5 秒打印内存使用
watch -n 5 'ps -p 31037 -o %mem,rss,comm && jcmd 31037 VM.native_memory summary'
```
### **4.2 触发 Full GC 观察内存回收**
```bash
# 手动触发 Full GC
jcmd 31037 GC.run
```
### **4.3 检查 OOM 风险**
```bash
# 查看 GC 日志(需开启 -Xloggc)
tail -f /usr/local/apache-tomcat-8.0.39/logs/gc.log
```
---
## **5. 结论**
- **主要问题**:
- **MetaSpace 占用高**(260MB),需限制 `MaxMetaspaceSize`。
- **线程栈占用大**(179MB),可减少 `-Xss` 或 `maxThreads`。
- **Direct Memory 可能泄漏**,需限制并检查 NIO Buffer。
- **Heap 内 `char[]` 和 `byte[]` 占用高**,需检查缓存和字符串处理逻辑。
- **优化后预期**:
- **RSS 内存下降 1-2GB**(从 5.26GB → 3.5GB 左右)。
- **减少 OOM 风险**,提高系统稳定性。
建议在 **低峰期重启 Tomcat** 并应用新 JVM 参数,持续监控内存变化。
root@shuncom-01:/usr/local/apache-tomcat-8.0.39/bin# jcmd 31037 VM.native_memory detail | grep "Java Heap\|Direct"
- Java Heap (reserved=4194304KB, committed=3646976KB)
[0x00000006c0000000 - 0x00000007c0000000] reserved 4194304KB for Java Heap from
root@shuncom-01:/usr/local/apache-tomcat-8.0.39/bin# jcmd 31037 VM.class_hierarchy | grep Generated
java.io.IOException: Command failed in target VM
at sun.tools.attach.LinuxVirtualMachine.execute(LinuxVirtualMachine.java:224)
at sun.tools.attach.HotSpotVirtualMachine.executeCommand(HotSpotVirtualMachine.java:217)
at sun.tools.attach.HotSpotVirtualMachine.executeJCmd(HotSpotVirtualMachine.java:199)
at sun.tools.jcmd.JCmd.executeCommandForPid(JCmd.java:150)
at sun.tools.jcmd.JCmd.main(JCmd.java:129)
root@shuncom-01:/usr/local/apache-tomcat-8.0.39/bin# jcmd 31037 GC.class_stats | head -20
31037:
GC.class_stats command requires -XX:+UnlockDiagnosticVMOptions
根据最新的诊断结果,以下是针对 Tomcat 进程 (PID 31037) 的 最终优化方案 和 关键结论:
---
## **1. 关键发现**
### **1.1 Java Heap 使用情况**
- **配置**:`-Xmx4096m` (4GB)
- **实际 Commit**:3.64GB(接近上限,存在 OOM 风险)
- **问题**:Heap 使用率过高,可能由于:
- 缓存未限制(如 HTTP Session 缓存)
- 大对象未及时释放(如 `char[]` 占 379MB)
### **1.2 Direct Memory**
- **未检测到 Direct Buffer 泄漏**(`grep "Direct"` 无输出)
- 但 `-XX:MaxDirectMemorySize=1024m` 仍可能被 NIO 或第三方库(如 Netty)使用,需保持限制。
### **1.3 MetaSpace**
- **无法直接获取类统计**(需 `-XX:+UnlockDiagnosticVMOptions`)
- **间接证据**:`Class Metadata` 占用 260MB,加载 37,630 个类,可能存在:
- 动态类加载(如 JSP 编译)
- 反射生成类(如 Spring AOP)
---
## **2. 最终优化方案**
### **2.1 修改 Tomcat JVM 参数**
编辑 `catalina.sh` 或 `setenv.sh`,替换原有参数:
```bash
JAVA_OPTS="
-Xms2048m -Xmx4096m
-XX:MaxMetaspaceSize=256m # 限制 MetaSpace 增长
-XX:MaxDirectMemorySize=512m # 限制堆外内存
-Xss256k # 减少线程栈大小
-XX:+UseG1GC # 改用 G1 垃圾回收器
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/usr/local/apache-tomcat-8.0.39/logs/gc.log
-XX:NativeMemoryTracking=detail # 持续监控 Native Memory
"
```
### **2.2 检查线程泄漏**
```bash
# 查看线程数
ps -L -p 31037 | wc -l
# 如果线程数 > 150,调整 Tomcat 的 maxThreads
sed -i 's/maxThreads="200"/maxThreads="100"/g' /usr/local/apache-tomcat-8.0.39/conf/server.xml
```
### **2.3 检查缓存问题**
```bash
# 检查 Tomcat 的 Session 超时设置(单位:分钟)
grep -A 1 "<session-config>" /usr/local/apache-tomcat-8.0.39/conf/web.xml
```
**建议**:确保 `<session-timeout>30</session-timeout>`(默认 30 分钟)。
### **2.4 重启 Tomcat 并监控**
```bash
# 重启 Tomcat
/usr/local/apache-tomcat-8.0.39/bin/shutdown.sh
/usr/local/apache-tomcat-8.0.39/bin/startup.sh
# 监控内存变化
watch -n 5 'ps -p $(pgrep -f "Bootstrap start") -o %mem,rss,comm'
```
---
## **3. 预期优化效果**
| 指标 | 优化前 | 优化后目标 |
|---------------------|----------------------|--------------------|
| **RSS 内存** | 5.26GB | 3.5-4GB |
| **线程栈内存** | 179MB (179 threads) | 25MB (100 threads) |
| **MetaSpace** | 260MB (无限制) | ≤256MB |
| **Direct Memory** | 可能泄漏 (1GB) | ≤512MB |
---
## **4. 后续维护建议**
1. **定期检查 GC 日志**:
```bash
tail -f /usr/local/apache-tomcat-8.0.39/logs/gc.log | grep -E "Full GC|OOM"
```
2. **设置内存报警**(如 Prometheus + Grafana):
- 报警规则:`RSS > 3.5GB` 持续 10 分钟。
3. **升级 JDK**:
- 当前使用 **JDK 1.8.0_111**(旧版,存在已知内存问题),建议升级到 **1.8.0_381+**。
---
## **5. 紧急情况处理**
如果优化后仍出现 **OOM**:
1. **快速 Dump 内存**:
```bash
jmap -dump:live,format=b,file=/tmp/tomcat_heap.hprof 31037
```
2. **分析内存泄漏**:
- 使用 [Eclipse MAT](https://www.eclipse.org/mat/) 分析 `tomcat_heap.hprof`。
---
**结论**:通过限制 MetaSpace、减少线程栈大小、优化 JVM 参数,**预计可降低 1-2GB 内存占用**,显著减少 OOM 风险。建议在低峰期实施并持续监控。
No Comments