
JVM启动后默认将最大使用堆大小设置为物理内存的为啥四分之一,譬如一台普通的应用x86服务器配置128G内存,那么启动在容器的迁移器后内启动JVM会将自己最大允许使用的堆内存调整为32G内存,如果容器启动时设置JVM只允许使用4G大小的到容内存,那么当JVM使用内存超过4G后,为啥将会导致内核杀死JVM。应用测试代码如下:
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内存的应用情况下,运行一段时间后直接被内核杀死。亿华云计算迁移器后输出如下:
# JAVA MemEat . . . free memory: 1307309488 free memory: 1306260896 free memory: 1305212304 free memory: 1304163712 free memory: 1303115120 Killed 为了避免这种情况,到容可以通过“ -Xmx ”设置最大堆内存后再次运行。为啥
# 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版本之后添加了动态调整的功能,能够根据用户设定的内存限制动态调整,启动参数如下:
# JAVA -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap MemEat 当我们修改了内存参数后JVM便可以随之调整。JAVA对于容器的支持不断增强到最新的JAVA 10版本后,已经原生支持容器环境,无需添加任何参数。云南idc服务商不仅如此,新版JAVA 10还支持CPU在容器内动态调整。如下所示JVM调整内存最大堆:
# docker run -it -m 1024M --entrypoint bash openjdk:11-jdk # java -XX:+PrintFlagsFinal -version | grep MaxHeapSize size_t MaxHeapSize = 268435456 可以看到上面的最大堆调整到内存限制的四分之一,而非物理内存的四分之一。还可以支持CPU自适应,如下所示:
# docker run -it --CPUs 2 ---entrypoint bash openjdk:11-jdk jshell> Runtime.getRuntime().availableProcessors() $1 ==> 2 可以看到通过JAVA的API成功地获取到当前设置的CPU个数。
如果是其他编程语言希望获取到容器的CPU和内存限制,可以通过容器内的cgroup文件系统,如获取容器内存的限制:
# cat /sys/fs/cgroup/memory/memory.limit_in_bytes 104857600 【编辑推荐】
Java基础与项目实战之EasyUI+Maven+Shiro-SSM权限项目实战 【天眼】Java大规模分布式跟踪监控系统 面试官:说说对React refs 的源码库理解?应用场景? Java并发编程系列之一Thread简介