hello云胜

技术与生活

0%

通过k8s的deploy部署一个java程序。并设置了资源限制内存1G

1
2
3
4
5
6
7
resources:
limits:
cpu: 500m
memory: 1000Mi
requests:
cpu: 300m
memory: 1000Mi

当我们启动java进程不指定Xms和Xmx时,打印一下java进程信息

1
2
3
4
5
6
7
sh-4.2# jinfo -flags 1
Attaching to process ID 1, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.232-b09
Non-default VM flags: -XX:CICompilerCount=2 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=null -XX:InitialHeapSize=16777216 -XX:MaxHeapSize=262144000 -XX:MaxNewSize=87359488 -XX:MinHeapDeltaBytes=196608 -XX:NewSize=5570560 -XX:OldSize=11206656 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops
Command line: -javaagent:/agent/skywalking-agent.jar -Dskywalking.agent.service_name=s02136::demo -Dskywalking.collector.backend_service=swagent.skywalking.paas-sit.xxx.net:11800 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dumps/oom.dump

看到,默认是Xms是16777216,即16M。Xmx是262144000,即256M

可以看到计算的基础是容器的内存限制limit

之前有种说法是,容器环境,由于java获取不到容器的内存限制,只能获取到服务器的配置。

以前确实有这种情况,但是java为了更好的使用容器环境,在Java 10 引入了 +UseContainerSupport(默认情况下启用),通过这个特性,可以使得JVM在容器环境分配合理的堆内存。

后来这个特性也合入了JDK8U191版本,在JDK8U191版本之后的java8也是可以获取容器的内存限制的。

我们看看基础镜像的java版本:

1
2
3
4
sh-4.2# java -version
openjdk version "1.8.0_232"
OpenJDK Runtime Environment (build 1.8.0_232-b09)
OpenJDK 64-Bit Server VM (build 25.232-b09, mixed mode)

UseContainerSupport

-XX:+UseContainerSupport允许JVM 从主机读取cgroup限制,例如可用的CPU和RAM,并进行相应的配置。这样当容器超过内存限制时,会抛出OOM异常,而不是杀死容器。 该特性在Java 8u191 +,10及更高版本上可用。

注意,在191版本后,-XX:{Min|Max}RAMFraction 被弃用,引入了-XX:MaxRAMPercentage,其值介于0.0到100.0之间,默认值为25.0。

最佳实践

在应用的启动参数,设置 -XX:+UseContainerSupport,设置-XX:MaxRAMPercentage=75.0,这样为其他进程(debug、监控)留下足够的内存空间,又不会太浪费RAM。