Openjdk 8合理使用容器的内存资源

前言

将Java应用容器化虽然更好地解决了可移植性问题,但也存在着一些不友好的情况,比如低版本的JDK(低于Java 8u131)并不能识别 CGroup资源限制。这将导致JVM读取的是宿主机的全部CPU和内存,一但容器使用资源超过限制则会被docker杀死。

在 kubernetes 中,我们会显示在 yaml 文件中配置CPU、内存请求和限制,我们希望容器中的JVM进程能够自动识别到 CGroup 资源限制,获取到正确的内存和CPU信息从而自行动态调整。

JVM 参数配置

以下操作皆在一台 4C 16G 服务器上进行。

01 版本低于 8u131

JDK 版本低于 8u131 版本的 JVM 不会自动识别到 CGroup 资源限制,需要手动设置初始堆大小以及最大堆大小,否则会按照宿主机的全部内存设置默认值:

-配置最大堆大小 -Xmx,默认值:内存的1/4

-配置初始堆大小 -Xms,默认值:内存的1/64

未配置JVM参数

可以看到 Max. Heap Size (Estimated): 3.48G,未能正确识别 CGroup 资源限制

$ docker run --rm -m 2GB openjdk:8u121-alpine java -XshowSettings:vm -version

VM settings:

 Max. Heap Size (Estimated): 3.48G

 Ergonomics Machine Class: server

 Using VM: OpenJDK 64-Bit Server VM

openjdk version "1.8.0_121"

OpenJDK Runtime Environment (IcedTea 3.3.0) (Alpine 8.121.13-r0)

OpenJDK 64-Bit Server VM (build 25.121-b13, mixed mode)
配置JVM参数

配置 -Xmx 和 -Xms 后即可达到我们想要的结果

$ docker run --rm -m 2GB openjdk:8u121-alpine java -XshowSettings:vm -Xmx2000m -Xms2000m -version

 
VM settings:

 Min. Heap Size: 1.95G

 Max. Heap Size: 1.95G

 Ergonomics Machine Class: server

 Using VM: OpenJDK 64-Bit Server VM
 

openjdk version "1.8.0_121"

OpenJDK Runtime Environment (IcedTea 3.3.0) (Alpine 8.121.13-r0)

OpenJDK 64-Bit Server VM (build 25.121-b13, mixed mode)


02 8u131 及以上版本

从 8u131 版本开始支持 UseCGroupMemoryLimit

ForHeap 和 MaxRAMFraction 这两个选项,用 CGroupMemory 的大小作为 JVM heap size,MAXRAMFraction 是用来控制实际可用的内存数量的,比如设置为 1 的话就是 CGroupMemoryLimit 的全部,设置为 2 的话一半,3 的话就是 1/3,以此类推

未配置JVM参数

可以看到 Max. Heap Size (Estimated): 3.48G,未能正确识别 CGroup 资源限制

$ docker run --rm -m 2GB openjdk:8u131-alpine java -XshowSettings:vm -version


VM settings:

 Max. Heap Size (Estimated): 3.48G

 Ergonomics Machine Class: server

 Using VM: OpenJDK 64-Bit Server VM


openjdk version "1.8.0_131"

OpenJDK Runtime Environment (IcedTea 3.4.0) (Alpine 8.131.11-r2)

OpenJDK 64-Bit Server VM (build 25.131-b11, mixed mode)
配置JVM参数

配置

-XX:+UnlockExperimentalVMOptions、

-XX:+UseCGroupMemoryLimitForHeap 和

-XX:MaxRAMFraction=1 后即可达到我们想要的结果

$ docker run --rm -m 2GB openjdk:8u131-alpine java -XshowSettings:vm -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1 -version

VM settings:

 Max. Heap Size (Estimated): 1.78G

 Ergonomics Machine Class: server

 Using VM: OpenJDK 64-Bit Server VM 

openjdk version "1.8.0_131"

OpenJDK Runtime Environment (IcedTea 3.4.0) (Alpine 8.131.11-r2)

OpenJDK 64-Bit Server VM (build 25.131-b11, mixed mode)
03 8u191 及以上版本

从 8u191 开始引入了 java10+ 上的 UseContainerSupport 选项,而且是默认启用的,不用设置。同时 UseCGroupMemoryLimitForHeap 这个就弃用了,不建议继续使用,同时还可以通过 -XX:InitialRAMPercentage、-XX:MaxRAMPercentage、-XX:MinRAMPercentage 这些参数更加细腻的控制 JVM 使用的内存比率。比如一些 Java 程序在运行时会调用外部进程、申请 Native Memory 等,所以即使是在容器中运行 Java 程序,也得预留一些内存给系统的。所以 -XX:MaxRAMPercentage 不能配置得太大。

未配置JVM参数

可以看到未添加任何 JVM 参数即可正确识别到 CGroup 资源限制

$ docker run --rm -m 2GB openjdk:8u191-alpine java -XshowSettings:vm -version

VM settings:

 Max. Heap Size (Estimated): 455.50M

 Ergonomics Machine Class: server

 Using VM: OpenJDK 64-Bit Server VM

openjdk version "1.8.0_191"

OpenJDK Runtime Environment (IcedTea 3.10.0) (Alpine 8.191.12-r0)

OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)
配置JVM参数

使用 -XX:MaxRAMFraction 参数调整 Max. Heap Size 大小

$ docker run --rm -m 2GB openjdk:8u191-alpine java -XX:MaxRAMFraction=1 -XshowSettings:vm -version

VM settings:

 Max. Heap Size (Estimated): 1.78G

 Ergonomics Machine Class: server

 Using VM: OpenJDK 64-Bit Server VM


openjdk version "1.8.0_191"

OpenJDK Runtime Environment (IcedTea 3.10.0) (Alpine 8.191.12-r0)

OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)

使用 -XX:InitialRAMPercentage、-XX:MaxRAMPercentage、-XX:MinRAMPercentage 参数更加细腻的控制 JVM 使用的内存比率

$ docker run --rm -m 2GB openjdk:8u191-alpine java -XX:InitialRAMPercentage=40.0 -XX:MaxRAMPercentage=90.0 -XX:MinRAMPercentage=50.0 -XshowSettings:vm -version

VM settings:

 Max. Heap Size (Estimated): 1.60G

 Ergonomics Machine Class: server

 Using VM: OpenJDK 64-Bit Server VM

openjdk version "1.8.0_191"

OpenJDK Runtime Environment (IcedTea 3.10.0) (Alpine 8.191.12-r0)

OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)

参考资料

  • http://www.51gjie.com/java/551.html

  • https://zhuanlan.zhihu.com/p/140849800

  • https://my.oschina.net/neverforget/blog/4779579

  • https://sevenyu.top/2019/04/01/java-resources-limit.html

  • https://qingmu.io/2018/12/17/How-to-securely-limit-JVM-resources-in-a-container/

### 如何检查和优化 Java 服务器内存使用情况 #### 使用工具监控进程 为了识别具体的Java应用程序及其内存消耗,可以利用`top`或更高级的`htop`命令来实时查看各个进程的状态。这些工具能够展示每个进程中CPU与RAM的具体占用量,从而帮助定位到最耗费资源的应用实例[^2]。 ```bash top -b -n 1 | grep java ``` 此命令会输出一次性的`top`数据并过滤出所有的Java相关条目,方便快速浏览正在运行的服务以及它们各自的内存开销。 #### 分析特定Java应用的堆栈信息 当已经锁定目标进程后,进一步深入探究其内部运作机制变得至关重要。通过附加JVM自带的诊断工具如jstat、jmap等至该PID上可以获得更加详细的统计资料: - `jstat -gcutil <pid>` 显示垃圾回收概要。 - `jmap -heap <pid>` 展现当前堆分配状况。 上述操作有助于理解程序是否存在泄漏风险或是不合理的对象创建行为。 #### 调整Tomcat配置实现性能提升 针对部署于Apache Tomcat容器内的Web应用程序而言,在调整虚拟机选项方面有着较大的灵活性。编辑位于CATALINA_HOME/bin目录下的catalina.sh文件并向其中加入合适的JAVA_OPTS参数可完成此项工作[^4]: ```sh export JAVA_OPTS="-Xms512m -Xmx2g" ``` 这里设置了初始最小堆大小为512MB而最大可达2GB;当然实际数值需依据业务需求灵活设定。 #### 设置适当的环境变量 确保在执行任何Java应用程序之前正确指定了所需的JDK路径。特别是在多版本共存的情况下,应该优先考虑采用非交互式的Shell环境中定义好的$JAVA_HOME指向官方推荐使用的发行版(例如华为JDK 8/OpenJDK 8/11),以此保障兼容性和稳定性[^3]。 #### 定期审查日志记录 最后但同样重要的一点是要养成良好的习惯——定期查阅由应用程序产生的错误报告和其他形式的日志文档。从中往往能发现潜在的问题线索,并据此采取预防措施避免未来可能出现的风险。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值