温馨提示×

Tomcat日志中出现内存溢出如何解决

小樊
44
2025-12-25 11:57:28
栏目: 智能运维

Tomcat 内存溢出定位与解决

一、先判定 OOM 类型

  • 查看 catalina.outlocalhost..log 中的异常关键字,常见有:
    • Java heap space:堆内存不足,对象分配失败。
    • PermGen space:永久代不足(主要发生在 Java 7 及更早)。
    • Metaspace:元空间不足(Java 8+)。
    • unable to create new native thread:无法创建新线程,多与系统/容器线程数限制相关。
  • 结合日志时间点与业务高峰,判断是突发流量、部署后持续恶化,还是运行很久后逐步变差,从而决定是“扩容参数”还是“查泄漏/控并发”。

二、快速缓解与参数调整

  • 调整堆大小(适用于 heap space)
    • 原则:将 -Xms-Xmx 设为相同,避免运行期扩缩堆带来的抖动;一般将最大堆控制在物理内存的约 1/4,且不超过约 80%,为系统与其他进程留余量。
    • 示例(Linux,编辑 bin/catalina.sh,在合适位置追加到 JAVA_OPTS):
      • JAVA_OPTS=“$JAVA_OPTS -Xms2g -Xmx2g”
  • 调整元空间(适用于 Java 8+ 的 Metaspace)
    • 示例:
      • JAVA_OPTS=“$JAVA_OPTS -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m”
  • 调整永久代(适用于 Java 7 及更早的 PermGen)
    • 示例:
      • JAVA_OPTS=“$JAVA_OPTS -XX:PermSize=128m -XX:MaxPermSize=256m”
  • 选择合适的 GC(示例)
    • JAVA_OPTS=“$JAVA_OPTS -XX:+UseG1GC”
  • 修改后重启 Tomcat 并验证进程参数是否生效(如:ps -ef | grep tomcat)。上述做法可快速缓解因默认内存过小或元空间/永久代不足导致的问题。

三、按场景给出配置示例

  • Linux 解压版 Tomcat(catalina.sh)
    • 建议位置:在 “cygwin=false” 这一行之前添加(或追加到既有 JAVA_OPTS):
      • JAVA_OPTS=“$JAVA_OPTS -Xms2g -Xmx2g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -XX:+UseG1GC”
  • Windows 解压版(catalina.bat)
    • 在文件靠前位置(如 @echo off 之后)添加:
      • set JAVA_OPTS=-Xms2g -Xmx2g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -XX:+UseG1GC
  • Windows 安装版(服务方式,tomcat8w.exe)
    • 运行 tomcat8w.exeJava 选项卡 → 设置 Initial memory poolMaximum memory pool(如 2048、4096),保存并重启服务。
  • 验证
    • 重启后用命令查看进程参数与内存:ps -ef | grep tomcat;或在 /manager/status(需配置用户)查看 JVM Free/Total/Max memory

四、定位根因与长期治理

  • 监控与 GC 分析
    • 实时观察:使用 jstat -gcutil 1000 查看 YGC/YGCT、FGC/FGCT、GCT 等指标,判断是否频繁 Full GC 或 GC 时间过长。
    • 堆转储与泄漏分析:在问题发生时抓取堆快照(如 jmap -dump:live,format=b,file=heap.hprof ),用 MAT/VisualVM 分析大对象与支配树,定位泄漏来源(缓存、会话、静态集合、ThreadLocal、未关闭资源等)。
  • 应用与容器配置优化
    • 线程与连接:在 server.xml 中合理设置 maxThreads、minSpareThreads、acceptCount,避免并发过高导致线程与内存压力叠加;必要时降低峰值并发、启用队列与限流。
    • 缓存与对象生命周期:控制本地缓存大小与过期策略,及时释放大对象、关闭 ResultSet/Statement/Connection,避免长生命周期引用。
    • 代码与依赖:排查重复加载类、热部署频繁导致的元空间压力,减少不必要的依赖与类生成。
  • 运维与预防
    • 建立基线监控(如 Prometheus + Grafana),对 Heap/Meta/线程数/GC 设置告警;定期回归压测与内存分析,必要时升级 JDK/Tomcat 稳定版本。

五、常见误区与建议

  • 只加内存不治本:堆/元空间扩容能“顶住”一阵,但若存在泄漏或设计问题,最终仍会 OOM;应结合 堆转储 + 分析 根治。
  • 过度分配堆:将 -Xmx 设得过大(如接近或超过物理内存的 80%)会引发系统 swap、GC 停顿过长与稳定性风险;应结合负载与 GC 表现逐步调优。
  • 忽视线程与连接:高并发带来线程栈与对象压力,需与 maxThreads/acceptCount 及后端连接池联动治理,而非单靠堆扩容。

0