温馨提示×

Linux Tomcat如何解决内存溢出

小樊
38
2025-12-08 22:36:00
栏目: 智能运维

Linux Tomcat 内存溢出定位与解决

一、先快速判断错误类型

  • 查看 $CATALINA_HOME/logs/catalina.out,根据异常关键字确定方向:
    • Java heap space:堆内存不足,对象申请超过 -Xmx 或存在内存泄漏。
    • PermGen space:JDK 7 及更早的永久代不足(常见于大量类/JSP 预编译、重复 jar)。
    • Metaspace:JDK 8+ 的类元数据区不足(对应 -XX:MaxMetaspaceSize)。
    • unable to create new native thread:系统资源/容器限制导致无法创建更多线程(如 ulimit -u、容器配额)。
  • 示例命令:tail -100 $CATALINA_HOME/logs/catalina.out | grep -i "OutOfMemoryError\|java.lang.OutOfMemoryError"

二、对应场景与解决方案

  • 堆内存不足(Java heap space)
    • 适度增大堆:设置 -Xms-Xmx(建议等值,如 -Xms2g -Xmx2g),并控制总堆不超过物理内存的约80%;可按需配置新生代 -Xmn≈-Xmx 的 1/4
    • 打开 GC 日志,观察是否频繁 Full GC 且回收效果差:-verbose:gc -Xloggc:$CATALINA_HOME/logs/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps
    • 发生 OOM 时获取堆转储:-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$CATALINA_HOME/logs/heap.hprof,用 Eclipse MAT/jvisualvm 分析泄漏对象与引用链。
  • 类元数据区不足(PermGen/Metaspace)
    • JDK 7 及更早:增大永久代(示例:-XX:PermSize=256m -XX:MaxPermSize=512m)。
    • JDK 8+:改用元空间(示例:-XX:MaxMetaspaceSize=512m;不设上限时受容器/系统内存约束)。
    • 减少重复 jar、避免应用间 jar 冲突,必要时将公共依赖放入 $CATALINA_HOME/shared/lib(视版本与类加载器而定)。
  • 线程创建失败(unable to create new native thread)
    • 检查系统/容器限制:ulimit -u(用户进程数)、ulimit -n(文件句柄);必要时调大并同步调整 Tomcat 的 maxThreads(在 conf/server.xml 的 Executor/Connector 中)。
    • 排查线程泄漏(线程数随时间持续增长):用 jstack 采样线程栈,定位创建线程的代码路径并修复。
  • 运行一段时间变慢或 OOM 反复
    • 检查日志滚动与磁盘占用,避免 catalina.out 无限增长(可用 cronolog 按时间切割)。
    • 排查业务侧问题:大对象/大文件一次性加载进内存、集合/缓存未及时清理、定时任务叠加等。

三、Linux 下正确修改 Tomcat 的 JVM 参数

  • 解压版或前台启动:编辑 $CATALINA_HOME/bin/catalina.sh,在 cygwin=false 之前添加(示例为 JDK 8+):
    export JAVA_OPTS="$JAVA_OPTS -server \
      -Xms2g -Xmx2g \
      -Xmn512m \
      -XX:MaxMetaspaceSize=512m \
      -verbose:gc -Xloggc:$CATALINA_HOME/logs/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps \
      -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$CATALINA_HOME/logs/heap.hprof"
    
    • 注意:JDK 8+ 不要再用 -XX:PermSize/MaxPermSize(已被移除)。
  • systemd 服务方式:在 /etc/systemd/system/tomcat.service[Service] 段使用 EnvironmentExecStart 前置参数,例如:
    Environment="JAVA_OPTS=-server -Xms2g -Xmx2g -Xmn512m -XX:MaxMetaspaceSize=512m"
    ExecStart=/opt/tomcat/bin/startup.sh
    
    修改后执行:systemctl daemon-reload && systemctl restart tomcat
  • 验证是否生效:ps -ef | grep tomcat | grep -E "Xms|Xmx|MaxMetaspaceSize";或在 /manager/status(需配置用户)查看 Free/Total/Max memory

四、参数建议与常见坑

  • 建议基线(按机器内存与负载调整):
    • 堆:-Xms-Xmx 等值,通常设为可用内存的50%–60%,不超过约80%;新生代 -Xmn≈-Xmx/4
    • 元空间:-XX:MaxMetaspaceSize=…(如 512m/1g),避免无限制增长。
    • GC 日志与 OOM Dump:便于定位与复盘,生产建议长期开启(注意磁盘与性能影响)。
  • 常见坑
    • catalina.sh 中错误使用 JAVA_OPTS="$JAVA_OPTS …" 前未导出,或被其他脚本覆盖;建议统一在脚本前部 export 且避免重复拼接。
    • -Xms/-Xmx 设得过大导致系统内存紧张、换页/GC 压力过大;或过小引发频繁 Full GC。
    • 误把 PermSize/MaxPermSize 用于 JDK 8+;JDK 8+ 应使用 Metaspace 相关参数。

0