Ubuntu 上运行 Java 应用时,内存参数往往直接影响启动稳定性、GC 频率和整体响应时间。很多问题并不是“内存不够”这么简单,而是堆大小、代际比例、垃圾回收器和应用代码行为没有配合好。下面按“先理解结构,再配置参数,最后用监控和验证收敛”的顺序梳理一遍,帮助你判断该怎么设、为什么这么设。
先看清 JVM 内存结构
在调整参数之前,先分清 JVM 内存里哪些区域和你的配置直接相关。
堆内存是调优重点
堆内存(Heap)主要用来存储对象实例,也是 GC 的主要工作区域。通常又分成两块:

- 新生代(Young Generation):新创建的对象大多先进入这里。如果新生代太小,短生命周期对象稍微一多,就可能频繁触发 Minor GC。
- 老年代(Old Generation):存活时间较长的对象会进入这里。老年代压力大时,更容易出现 Full GC,停顿成本也更高。
非堆内存也不能忽略
非堆内存(Non-Heap)主要保存类信息、常量、静态变量,以及 JIT 编译后的代码等内容。老版本 JVM 里常提到 PermGen,新版本已经移除。虽然日常参数调整多围绕堆展开,但类加载很多、框架较重或运行时间很长的服务,也要留意非堆区域的占用情况。

这一步的意义在于,后面看到 -Xms、-Xmx、-Xmn 和 GC 选项时,你能知道它们分别在影响哪一部分,而不是靠经验盲调。
几个最常用的 JVM 内存参数怎么理解
Ubuntu 上启动 Java 程序时,最常见的内存参数基本都集中在这几项。
-Xms:JVM 启动时的初始堆大小。比如-Xms512m表示初始堆为 512MB。-Xmx:堆的最大大小。比如-Xmx2g表示最大堆为 2GB。-Xmn:新生代大小。比如-Xmn512m。-XX:NewRatio:新生代与老年代的比例。比如-XX:NewRatio=2表示新生代:老年代 = 1:2。- 垃圾回收器选择:高吞吐量场景可以考虑
-XX:+UseParallelGC,低延迟场景可以考虑-XX:+UseG1GC,其中 G1 GC 是 JDK 9+ 默认方案。
-Xms 和 -Xmx 为什么常建议设成一致
如果两者差距较大,JVM 运行过程中可能发生堆扩容,这会带来额外开销。对长期运行的服务,通常建议让 -Xms 和 -Xmx 保持一致,这样内存边界更稳定,也更容易观察真实负载下的表现。
新生代不宜一味调大
-Xmn 太小,会导致 Minor GC 频繁;但太大也未必更好,因为它会挤压老年代空间,甚至影响 Full GC 的表现。一般可先按堆内存的 1/3 到 1/2 作为起点,再结合监控结果调整。
GC 选择要看目标,不是看“新旧”
如果更看重吞吐量,-XX:+UseParallelGC 往往更直接;如果更在意停顿时间和服务响应,-XX:+UseG1GC 通常更合适。参数没有绝对最优解,关键在于你的应用是批处理、接口服务,还是交互式系统。
Ubuntu 中常见的 Java 内存设置方式
参数怎么理解是一回事,真正落到 Ubuntu 环境里,通常有三种常见配置方法。
命令行临时设置:适合测试和单次运行
最直接的方法,是在启动命令里显式写入参数:
java -Xms512m -Xmx2g -XX:+UseG1GC -jar your-app.jar
这条命令的含义很清楚:初始堆 512MB、最大堆 2GB,并使用 G1 垃圾回收器。它适合临时验证参数效果,也方便和监控工具配合观察。
环境变量永久设置:适合固定部署习惯
如果一台机器上长期以相似方式启动应用,可以把参数写入环境变量:
# 打开环境变量文件
sudo nano /etc/environment
# 添加以下内容(根据需求调整数值)
JAVA_OPTS="-Xms1g -Xmx4g -XX:+UseG1GC"
# 保存后重新加载
source /etc/environment
之后启动时可以直接执行:
java $JAVA_OPTS -jar your-app.jar
这种方式的优点是统一、易复用,但前提是你的启动脚本或服务定义会正确读取这个变量。
应用服务内设置:Tomcat 和 Eclipse 是典型场景
如果 Java 程序不是手工运行,而是挂在容器或 IDE 中,参数通常要写到对应位置。
Tomcat 可以编辑 /opt/tomcat/bin/catalina.sh,在文件开头加入:
export JAVA_OPTS="-Xms1g -Xmx4g -XX:+UseG1GC"
保存后重启服务:
sudo systemctl restart tomcat
Eclipse 中则可以进入 Run → Run Configurations,在 Arguments 标签页的 VM arguments 中填写:
-Xms512m -Xmx2g -XX:+UseG1GC
点击 Apply 后再运行即可。
参数调完后,怎么监控和继续调优
内存设置不能只看“能不能启动”,还要看运行过程中的 GC 频率、堆占用变化和对象分布。

jstat:适合快速看 GC 统计信息,例如每秒输出一次:jstat -gc。1000 jconsole:图形化查看内存、线程、类加载等运行状态,直接执行jconsole后选择目标 Java 进程即可。VisualVM:比 jconsole 更全面,支持内存分析、线程分析等。Ubuntu 上可通过apt install visualvm安装。jmap:适合查看堆内存详情,例如执行jmap -heap。
GC 日志是判断参数是否合理的重要依据
如果要进一步分析停顿和回收频率,可以在启动时增加 GC 日志:
java -Xms1g -Xmx4g -XX:+UseG1GC -Xlog:gc*:file=gc.log -jar your-app.jar
日志能帮助你判断 Minor GC 是否过于频繁、Full GC 是否出现异常,以及暂停时间是否超出预期。和只盯着堆大小相比,这类数据更能说明问题出在哪里。
除了参数,代码和容量边界也要一起看
很多内存问题最后都会落回代码本身。如果对象创建过多、缓存使用失控,单纯扩大 -Xmx 只能延后问题暴露。
代码层面可以优先检查这几件事
- 减少对象创建:避免在循环中反复创建临时对象,字符串拼接可优先使用
StringBuilder,必要时考虑对象池复用。 - 选择合适的数据结构:例如
HashMap适合快速查找,ArrayList适合随机访问,不要在不合适的场景里强行使用LinkedList。 - 使用缓存但控制边界:对高频访问结果可以使用缓存,例如
Caffeine、Guava Cache,但缓存本身也必须设置容量和淘汰策略。 - 避免内存泄漏:及时关闭数据库连接和 IO 流;对于非关键对象,例如缓存项,可以视情况使用
WeakReference,给 GC 留出回收空间。
实际部署时要守住两个边界
- 不要过度分配内存:
-Xmx一般不建议超过系统可用内存的 70%。例如 16GB 内存机器上,通常建议-Xmx不超过 12GB,避免把系统和其他进程挤压得过紧。 - 参数调整后必须验证:可以通过压力测试工具,例如
JMeter,模拟高并发负载,再结合 GC 日志和监控曲线确认修改是否真的有效。
总结来看,Ubuntu 下的 Java 内存设置没有“一组通用最佳参数”。更稳妥的做法是先理解 JVM 内存结构,再根据业务目标确定堆大小和 GC 方案,随后用 jstat、jconsole、VisualVM、GC 日志和压测结果反复校准。这样调出来的参数,才更接近真实生产环境需要。







