CATALINA_OPTS和JAVA_OPTS的核心区别在于:前者仅在Tomcat启动/运行时生效且不传递给子进程,更安全;后者还作用于stop命令并被所有子JVM共享,易引发干扰。

直接改 catalina.sh 或 catalina.bat 里的 JAVA_OPTS 或 CATALINA_OPTS 是最有效的方式,其他地方(如 IDE 启动配置、系统环境变量)容易被忽略或覆盖,导致配置不生效。
为什么改了环境变量还是报 OutOfMemoryError: Java heap space
常见现象是:明明在 /bin/catalina.sh 里加了 JAVA_OPTS="-Xms1g -Xmx2g",启动后用 jstat -gc <pid> 查看,发现堆大小仍是默认值(比如 256M)。
- Tomcat 8.5+ 默认优先读取
CATALINA_OPTS,而忽略JAVA_OPTS—— 如果你只改了JAVA_OPTS,它根本不会被加载 - Windows 下
catalina.bat中的set JAVA_OPTS=...必须放在所有call和goto之前,否则会被后续逻辑清空 - Linux 下
catalina.sh中的JAVA_OPTS定义如果写在注释块里、或被if条件包裹但条件不满足,也会跳过 - 某些 Linux 发行版的 systemd 服务脚本会绕过
catalina.sh,直接调用java命令,此时必须在.service文件里显式设置Environment="CATALINA_OPTS=..."
CATALINA_OPTS 和 JAVA_OPTS 到底该用哪个
结论很明确:一律用 CATALINA_OPTS。原因不是“推荐”,而是行为差异:
-
CATALINA_OPTS只在 Tomcat 启动时生效,且不会传递给子进程(比如 JSP 编译器),更安全 -
JAVA_OPTS会被 Tomcat 和所有它 fork 的子 JVM 共享,可能干扰shutdown.sh等工具的内存行为 - 官方文档和主流部署实践(包括 Docker 镜像、Ansible role)都默认使用
CATALINA_OPTS - 示例(Linux):
CATALINA_OPTS="-Xms1g -Xmx2g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m -Xss256k"
Java 8+ 必须换掉 PermSize,否则参数无效
如果你还在用 -XX:PermSize=128m -XX:MaxPermSize=256m,这些参数在 Java 8 及以后版本中完全被忽略,JVM 启动日志里会出现警告,而且元空间(Metaspace)会无限增长直到触发 OutOfMemoryError: Metaspace。
- Java 7 及以前:用
-XX:PermSize/-XX:MaxPermSize - Java 8+:必须换成
-XX:MetaspaceSize和-XX:MaxMetaspaceSize,否则等于没配 - 典型错误场景:从 Java 7 升级到 Java 11 后未更新 JVM 参数,结果旧配置失效,新 Metaspace 不设上限,类加载器泄漏直接打爆内存
- 建议值:
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m(根据实际加载的 class 数量微调)
线程数太多也会触发 OutOfMemoryError: unable to create new native thread
这个错误不是堆不够,而是操作系统层面的线程资源耗尽,常被误判为“内存溢出”。尤其在高并发 + 小栈配置下极易发生。
- 每个线程默认占 1MB 栈空间(
-Xss默认值),500 个线程就是 500MB;若设成-Xss256k,同样线程数只占 125MB - Linux 上可通过
ulimit -u查看当前用户最大线程数限制,cat /proc/<pid>/status | grep Threads查看已用线程数 - Tomcat 的
maxThreads(默认 200)和-Xss必须协同调整:比如设maxThreads=500,就别再用默认-Xss1m,否则光线程栈就吃掉 500MB - 生产环境建议:
-Xss256k+maxThreads=300+minSpareThreads=20,比盲目堆内存更治本
真正关键的点往往藏在细节里:比如 CATALINA_OPTS 被 systemd 忽略、Java 版本升级后还沿用 PermSize、或者把 -Xss 设得太小导致 StackOverflow 而非 OOM —— 这些都不是“配少了内存”,而是配置没对路。排查时先确认 JVM 实际生效参数(ps aux | grep java),再对照 Java 版本查参数有效性,比反复调大 -Xmx 更可靠。

















