Tomcat内存配置的核心答案是:修改bin目录下的catalina.sh(Windows系统为catalina.bat)文件,在JAVA_OPTS变量中设置-Xms和-Xmx参数,推荐最大堆内存设为物理内存的50%左右,同时根据JDK版本配置元空间大小。本文围绕tomcat内存溢出怎么解决、catalina.sh配置位置、JVM参数调优等实际场景展开,给出可直接落地的操作步骤。
tomcat内存怎么配置才合理?先看懂这几个JVM参数
很多人在服务器上装好Tomcat就直接扔进生产环境,等到某个深夜流量一上来,应用卡死、报OutOfMemoryError,才想起内存配置这回事,Tomcat本身是一个Servlet容器,它不直接管理内存,所有对象实例都运行在JVM(Java虚拟机)堆内存中,所以调整Tomcat内存,本质就是调整JVM的启动参数。
堆内存三大参数:Xms、Xmx、Xmn
- -Xms:JVM启动时初始分配的堆内存大小,如果设置过小,应用启动后会频繁触发GC(垃圾回收)来扩容,浪费CPU资源。
- -Xmx:JVM允许的最大堆内存大小,这是最关键的一个参数,当应用需要的内存超过这个值,就会抛出
java.lang.OutOfMemoryError: Java heap space。 - -Xmn:新生代内存大小,新生代存放短命对象,适当调大可以减少Minor GC频率,但也不能过大,否则老年代空间被压缩,Major GC会更频繁。
行业共识认为,-Xms和-Xmx建议设为相同值,避免运行期动态伸缩带来的性能损耗。
JDK8之后的元空间参数
PermGen(永久代)在JDK8里已经被移除,如果你还在使用JDK7及以下版本,需要配置-XX:PermSize和-XX:MaxPermSize,JDK8及以上版本则用-XX:MetaspaceSize和-XX:MaxMetaspaceSize,没有绝对的通用数字,要看部署的都是什么应用,走了哪些逻辑,有没有大量动态生成类的场景。
一个合理的参数组合长什么样
以一台8G内存的服务器、部署单个Tomcat实例为例,一般这样设置:
| 参数 | 推荐值 | 说明 |
|---|---|---|
| -Xms | 2048m | 初始堆内存,与Xmx一致 |
| -Xmx | 2048m | 最大堆内存,约占物理内存25% |
| -Xmn | 512m | 新生代,约为堆的1/4 |
| -XX:MetaspaceSize | 256m | 元空间初始大小 |
| -XX:MaxMetaspaceSize | 512m | 元空间上限 |
如果一台机器部署了多个Tomcat实例,总的堆内存加元空间,配合JVM自身开销,尽量控制在物理内存的60%~70%以内,给操作系统留余量。
tomcat内存溢出怎么解决?一步步排查与实操
遇到内存溢出别慌,先看报错日志,Tomcat的日志默认在logs/catalina.out文件里,用tail -100查看末尾输出,不同类型的报错对应不同的解决方向。
区分三类常见报错
java.lang.OutOfMemoryError: Java heap space:堆内存不够,对象实例撑满了整个堆,优先检查代码里是不是有大List、大Map缓存未释放,再考虑扩容-Xmx。java.lang.OutOfMemoryError: Metaspace:元空间溢出,常见于使用CGLIB、反射生成大量代理类的框架,比如Spring AOP、MyBatis,调大MaxMetaspaceSize,同时检查类加载器是否存在泄漏。java.lang.OutOfMemoryError: unable to create new native thread:线程数达到系统上限,这不是内存参数问题,是Linux系统进程数限制,需要调整ulimit -u或/etc/security/limits.conf。
修改catalina.sh文件的具体步骤
找到Tomcat安装目录下的bin/catalina.sh,用vim编辑,在注释区域之后找到JAVA_OPTS的赋值位置,如果文件里没有现成的JAVA_OPTS行,自行添加一行:
JAVA_OPTS="-server -Xms2048m -Xmx2048m -Xmn512m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -XX:+UseConcMarkSweepGC -XX:+HeapDumpOnOutOfMemoryError"
-server:使用服务端模式,启动更慢但运行期性能更好。-XX:+HeapDumpOnOutOfMemoryError:内存溢出时自动导出堆快照文件,方便事后分析。- 堆快照文件默认生成在Tomcat的bin目录,文件名为
java_pidXXXX.hprof,可以使用MAT工具分析对象引用链。
修改完成后,必须重启Tomcat才能生效,方式为进入bin目录执行./shutdown.sh然后./startup.sh。
确认配置是否生效
重启后运行jps -l查看Java进程PID,然后执行jmap -heap <PID>,输出的MaxHeapSize就是实际生效的-Xmx值,如果显示的数字和设置不一致,检查是否被setenv.sh文件里的JAVA_OPTS覆盖这是Linux下Tomcat另一种常见配置方式,优先级高于catalina.sh中的默认值。
tomcat优化配置参数对比:单实例与多实例部署的差异
单实例部署和多实例部署在内存分配思路上有本质区别,绝对不能照搬同一套参数。
单实例部署方案
适合绝大多数中小型Web应用,一台机器只跑一个Tomcat实例,配置上比较宽松,推荐组合为:
- -Xmx设为物理内存的50%左右
- -Xmn设为Xmx的1/3到1/4
- GC策略使用G1收集器(JDK11+默认)或CMS(JDK8)
多实例部署方案
一个服务器跑多个Tomcat实例,常见于需要隔离不同业务的情况,此时每个实例的-Xmx要按实例数量均摊,比如4G内存跑两个实例,每个Xmx最多给1536m,留出约1G给操作系统和JVM非堆开销,实际运维中,部署了哪些业务、高峰期各实例的负载差距,都直接影响分配结果。
核心原则是给操作系统留足余量,如果物理内存全部被堆占满,操作系统会频繁使用swap分区,应用响应时间会急剧恶化,比内存溢出的后果更难排查。
配合Nginx做动静分离时的配置要点
当Tomcat前边挂了Nginx做静态资源代理,Tomcat的堆内存压力会明显下降,因为图片、CSS、JS这些静态文件不再经过Tomcat,堆内存只处理业务逻辑和Session数据,这种情况下,-Xmx可以适当调低,省出的内存留给Nginx的缓存,或者提高Tomcat的最大线程数。
实际部署中还有一层考虑JVM运行时会额外消耗堆外内存(Direct Memory、线程栈),这部分不受-Xmx控制,Xmx永远不要等于物理内存容量。
tomcat启动参数配置里的细节陷阱
setenv.sh比catalina.sh更安全
官方推荐的做法不是直接改catalina.sh,而是在bin目录下新建setenv.sh中写JAVA_OPTS,这么做的好处很明显:Tomcat升级时如果覆盖了catalina.sh,自定义参数不会丢失,脚本内容示例:
export JAVA_OPTS="-server -Xms1024m -Xmx1024m -XX:+UseG1GC"
放在setenv.sh里的配置会自动被catalina.sh调用执行。
Windows下的tomcat内存配置
Windows系统对应的文件是bin/catalina.bat,修改逻辑相同,在文件中找到set "JAVA_OPTS=%JAVA_OPTS% ..."这一段,在老版本Tomcat中是没有这段配置的,直接把参数加到set JAVA_OPTS=...中。
位置在Tomcat安装路径下的bin目录,有中文或空格路径时,JVM参数不需要加引号,但脚本里的路径变量要检查是否正确。
查看GC日志验证调整效果
在JAVA_OPTS追加:
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/opt/tomcat/logs/gc.log
根据GC日志可以判断新生代和老年代使用频率,如果Young GC过于频繁且每次回收后堆占用率仍高,说明-Xmn偏小;如果Old GC频繁,说明大量对象存活时间偏长,应该检查代码或者调大-Xmx,靠数据验证判断,不加推断或猜测。
tomcat常见配置中与内存相关的其他参数
maxThreads线程池设置
不要忽略线程数对内存的影响,每创建一个线程,JVM就会分配一块线程栈空间(默认1MB),如果maxThreads设置过大,比如400,同时活跃线程有200个,光是线程栈就占掉200MB堆外内存,排查数据时留意java.lang.OutOfMemoryError: unable to create new native thread,往往就是线程数+堆内存一起把系统内存耗尽的。
连接器BIO与NIO的选择
Tomcat 9之后默认使用NIO,线程模型更高效,显著降低空闲连接时的内存占用,如果还在用老版本且没有特殊原因,通过修改server.xml中Connector节点的protocol属性切换为NIO。
在server.xml中配置内存相关参数
server.xml不直接控制JVM内存,但<Connector>标签里的maxThreads、minSpareThreads、acceptCount间接影响内存水位,空闲线程越多,占用的栈内存越大,建议minSpareThreads保持默认值即可。
Q&A:tomcat内存配置常见疑问
tomcat内存设置多大合适?
没有统一数值,需要综合部署的业务规模、单次请求数据量、并发峰值来判断,保守做法是堆内存不超过物理内存的一半,观察几天GC日志后再微调,多数情况下,初始-Xmx设置在2GB到4GB之间可以覆盖日常中小型应用的负载水平。
修改内存配置后Tomcat启动不了怎么办?
检查JAVA_OPTS的语法有无空格遗漏,确认-Xmx数值后边的单位(m或g)是否正确,运行./catalina.sh configtest可以做基础校验,如果还是不行,删除新增参数逐项排查,另外确认JDK版本,JDK8和JDK11支持的参数有差异,比如-XX:MaxPermGen在JDK8以后就直接报错。
线上服务器tomcat频繁出现GC停顿,是内存配置还是代码问题?
GC停顿时间较长一般说明堆内存分配策略不匹配,建议先用jstat -gcutil <PID> 1000连续观察各区域使用情况,如果在Full GC后老年代占用率依然居高不下,说明存活对象太多,此时调大Xmx只能延后问题爆发,排查代码才是根治路径;如果Full GC后内存回收明显,调大-Xmn或切换G1收集器就能改善响应时间。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/582652.html




