部署java应用到容器

java 8u131之后的版本开始支持容器特性,之前的版本中并不支持容器相关的特性。

java基础知识

JVM默认的最大堆内存大小为系统内存的1/4,可以使用参数-XX:MaxRAMFraction=1表示将所有可用内存作为最大堆。

cgroup的限制在docker中能够看到,通过查看/sys/fs/cgroup目录下的文件可以获取。

JVM的用户地址空间分为JVM数据区和direct memory。JVM数据区由heap、stack等组成,GC是操作的这一片内存。direct memory是额外划分出来的一片内存空间,需要手工管理内存的申请和释放。

direct memory使用Unsafe.allocateMemoryUnsafe.setMemory来申请和设置内存,是直接使用了C语言中的malloc来申请内存。由jvm参数MaxDirectMemorySize来限制direct memory可使用的内存大小。

java < 8u131

没有对容器的任何支持,对cpu和内存的限制需要通过jvm的参数来配置。

java中并不能看到内存资源的限制,会存在使用内存超过限制而被OOM的问题。可通过在程序中设置-Xmx来解决该问题。

JVM GC(垃圾对象回收)对Java程序执行性能有一定的影响。默认的JVM使用公式“ParallelGCThreads = (ncpus <= 8) ? ncpus : 3 + ((ncpus * 5) / 8)” 来计算做并行GC的线程数,其中ncpus是JVM发现的系统CPU个数。一旦容器中JVM发现了宿主机的CPU个数(通常比容器实际CPU限制多很多),这就会导致JVM启动过多的GC线程,直接的结果就导致GC性能下降。Java服务的感受就是延时增加,TP监控曲线突刺增加,吞吐量下降。

显式的传递JVM启动参数-XX:ParallelGCThreads告诉JVM应该启动几个并行GC线程。它的缺点是需要业务感知,为不同配置的容器传不同的JVM参数。

java9 and java >= 8u131

增加了XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap参数来检查内存限制。JVM中可以看到cgroup中的内存限制。

可以根据容器中的cpu限制来动态设置GC线程数,不再需要单独设置-XX:ParallelGCThreads

java10

jvm参数XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap已经默认开启,但新增加-XX:-UseContainerSupport参数来更好支持容器,支持内存和cpu。

在开启-XX:-UseContainerSupport的同时,XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap会被关闭。

ref