在 2 核 2G(2 vCPU, 2GB RAM)的服务器上部署 Java Web 项目,资源非常紧张。如果直接运行默认配置的 JVM 和中间件,极易出现 OOM(内存溢出)、Full GC 频繁甚至服务假死。
以下是针对该配置需要重点优化的参数和策略,按优先级排序:
1. JVM 核心参数优化(最关键)
Java 进程是内存消耗大户。在 2GB 总内存下,操作系统和中间件(如 Tomcat/Nginx)需要预留空间,留给 JVM 的实际可用内存通常只有 1GB ~ 1.4GB。
A. 堆内存设置 (-Xms 和 -Xmx)
- 原则:堆内存必须小于物理总内存的 60%-70%,防止操作系统因交换分区(Swap)导致性能急剧下降。
- 推荐值:
-Xms512m -Xmx512m(或最大不超过768m)- 注意:将初始堆和最大堆设置为相同值,避免 JVM 动态调整堆大小带来的性能抖动。
B. 元空间 (-XX:MetaspaceSize)
- 背景:JDK 8+ 使用 Metaspace 存储类元数据,默认会随应用加载类数量增长。
- 优化:限制其上限,防止无限占用内存。
- 推荐值:
-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m
C. GC 垃圾回收器选择
- 问题:默认的 Parallel GC 或 CMS 在低内存下可能产生较长的 STW(Stop-The-World)。
- 优化:
- JDK 8:推荐使用 G1 GC(虽然 G1 在极小内存下开销略大,但可控性更好),或者保持默认但开启并发标记。
- JDK 11+:G1 是默认且推荐的选择。
- 命令示例:
-XX:+UseG1GC
- 进阶:如果应用对延迟极其敏感且内存极低,可尝试 ZGC(JDK 11+),但在 2G 机器上需谨慎测试。
D. 线程栈大小 (-Xss)
- 原理:每个线程占用固定栈内存。2 核 CPU 意味着并发线程数不宜过多。
- 优化:减小默认值(通常为 1MB),以支持更多线程。
- 推荐值:
-Xss256k或-Xss512k
E. 关闭不必要的诊断功能
- 生产环境建议关闭堆转储文件生成,避免磁盘 IO 压力。
- 命令:
-XX:+DisableExplicitGC(防止代码中手动调用 System.gc())
2. 中间件参数优化
A. Tomcat / Jetty
- Connector 线程池:
maxThreads:默认 200,2G 机器建议降至 50-100。minSpareThreads:建议设为 10-20。acceptCount:队列长度,建议 50-100。
- HTTP/2:如果不需要 HTTP/2 特性,请禁用以减少内存开销。
- NIO vs BIO:确保使用 NIO 模式(默认),并适当调优
selectorTimeout。
B. 数据库连接池 (Druid/HikariCP)
- HikariCP:
maximumPoolSize:这是关键。2 核 CPU 无法处理高并发 DB 请求,建议设为 10-20。过大不仅抢 CPU,还会导致上下文切换频繁。minimumIdle:设为与maximumPoolSize一致或略小,避免频繁创建销毁连接。
- Druid:
maxActive:同样限制在 20 以内。- 开启
removeAbandoned检测泄露连接。
3. 操作系统层面优化
A. 虚拟内存 (Swap)
- 现状:2G 机器如果没有 Swap,一旦内存吃紧会触发 OOM Killer 杀死进程。
- 操作:务必创建 1GB – 2GB 的 Swap 分区作为缓冲。
# 示例:创建 2G swap 文件 dd if=/dev/zero of=/swapfile bs=1M count=2048 chmod 600 /swapfile mkswap /swapfile swapon /swapfile - 注意:调整
vm.swappiness参数,让系统更倾向于使用物理内存,仅在必要时才用 Swap(例如设为 10 或 60,视具体负载而定)。
B. 文件描述符限制 (ulimit)
- Java 应用在高并发下会打开大量文件和网络连接。
- 操作:修改
/etc/security/limits.conf。* soft nofile 65535 * hard nofile 65535
C. 内核参数 (sysctl.conf)
- 增加 TCP 端口范围,减少 TIME_WAIT 状态的影响。
net.ipv4.ip_local_port_range = 1024 65535 net.core.somaxconn = 1024 net.ipv4.tcp_tw_reuse = 1
4. 架构与应用层优化
如果单机资源实在无法满足需求,仅靠参数调整有极限,必须考虑架构降级:
- 启用压缩:在 Nginx 或 Tomcat 开启 Gzip 压缩,减少网络传输量,降低 CPU 计算压力(需权衡 CPU 与带宽)。
- 静态资源分离:将图片、CSS、JS 托管到 CDN 或对象存储(OSS/S3),不要让 Web 服务器处理这些 I/O。
- 缓存策略:
- 引入 Redis(如果内存允许,Redis 本身也需要约 200-300MB,若不行则使用本地缓存如 Caffeine/Guava Cache)。
- 开启数据库查询结果缓存。
- JVM 日志监控:
- 开启 GC 日志:
-Xloggc:/path/to/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps - 定期分析日志,观察 Full GC 频率。如果 Full GC 频繁(如每几分钟一次),说明堆内存依然不足或存在内存泄漏。
- 开启 GC 日志:
总结配置示例 (JDK 8+)
# 启动脚本示例
JAVA_OPTS="-server
-Xms512m -Xmx512m
-Xss256k
-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/var/log/java_heap.hprof
-XX:+DisableExplicitGC
-Djava.awt.headless=true"
# 结合 Tomcat 配置
# catalina.sh 中 export JAVA_OPTS="$JAVA_OPTS ..."
# server.xml 中 Connector maxThreads="80" minSpareThreads="10" acceptCount="100"
最后建议:在正式部署前,务必进行压测(如使用 JMeter),观察 CPU 使用率、GC 频率和响应时间,根据实际数据微调上述参数。2 核 2G 属于“勉强够用”的配置,任何突发流量都可能导致服务雪崩,因此限流(Rate Limiting)也是必要的防御手段。
PHPWP博客