本文由 简悦 SimpRead 转码, 原文地址 www.toutiao.com
JVM 启动后默认将最大使用堆大小设置为物理内存的四分之一,譬如一台普通的 x86 服务器配置 128G 内存,那么启动在容器的内启动 JVM 会将自己最大允
JVM 启动后默认将最大使用堆大小设置为物理内存的四分之一,譬如一台普通的 x86 服务器配置 128G 内存,那么启动在容器的内启动 JVM 会将自己最大允许使用的堆内存调整为 32G 内存,如果容器启动时设置 JVM 只允许使用 4G 大小的内存,那么当 JVM 使用内存超过 4G 后,将会导致内核杀死 JVM。测试代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import JAVA.util.ArrayList;
import JAVA.util.List;
public class MemEat {
public static void main(String[] args) {
List l = new ArrayList<>();
while (true) {
byte b[] = new byte[1048576];
l.add(b);
Runtime rt = Runtime.getRuntime();
System.out.println( "free memory: " + rt.freeMemory() );
}
}
}
|
代码非常简单,只是通过一个死循环不停地申请内存,如果是在 JAVA 8u111 版本之前,直接通过 docker run -m 100m 限制使用 100M 内存的情况下,运行一段时间后直接被内核杀死。输出如下:
1
2
3
4
5
6
7
8
|
# JAVA MemEat
. . .
free memory: 1307309488
free memory: 1306260896
free memory: 1305212304
free memory: 1304163712
free memory: 1303115120
Killed
|
为了避免这种情况,可以通过 “-Xmx” 设置最大堆内存后再次运行
1
2
3
4
5
6
7
|
# JAVA -Xmx100m MemEat
. . .
free memory: 8382264
free memory: 7333672
free memory: 6285080
free memory: 5236488
Exception in thread "main" JAVA.lang.OutOfMemoryError: JAVA heap space MemEat.main(MemEat.JAVA:8)
|
可以看到 JVM 由于堆内存不足,自己退出了。这种在 JVM 添加参数的方式有个弊端:如果修改了容器的内存限制,还需要调整启动参数。为此在 JAVA 8u144 版本之后添加了动态调整的功能,能够根据用户设定的内存限制动态调整,启动参数如下:
1
|
# JAVA -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap MemEat
|
当我们修改了内存参数后 JVM 便可以随之调整。JAVA 对于容器的支持不断增强到最新的 JAVA 10 版本后,已经原生支持容器环境,无需添加任何参数。不仅如此,新版 JAVA 10 还支持 CPU 在容器内动态调整。如下所示 JVM 调整内存最大堆:
1
2
3
|
# docker run -it -m 1024M --entrypoint bash openjdk:11-jdk
# java -XX:+PrintFlagsFinal -version | grep MaxHeapSize
size_t MaxHeapSize = 268435456
|
可以看到上面的最大堆调整到内存限制的四分之一,而非物理内存的四分之一。还可以支持 CPU 自适应,如下所示:
1
2
3
|
# docker run -it --CPUs 2 ---entrypoint bash openjdk:11-jdk
jshell> Runtime.getRuntime().availableProcessors()
$1 ==> 2
|
可以看到通过 JAVA 的 API 成功地获取到当前设置的 CPU 个数。
如果是其他编程语言希望获取到容器的 CPU 和内存限制,可以通过容器内的 cgroup 文件系统,如获取容器内存的限制:
1
|
# cat /sys/fs/cgroup/memory/memory.limit_in_bytes
|