Java 8 终于支持 Docker!
java 8曾经与docker无法很好地兼容性,现在问题已消失。
请注意:我在本文中使用采用gnu gpl v2许可证的openjdk官方docker映像。在oracle java se中,这里描述的docker支持功能在更新191中引入。oracle在2019年4月更改了java 8更新的许可证,自java se 8 update 211以来商业使用不再免费。
你是否遇到过在docker中运行的基于jvm的应用程序出现“随机”故障?或者也许是一些奇怪的死机?两者都可能是java 8(仍广泛使用的)中糟糕的docker支持引起的。
docker使用控制组(cgroups)来限制资源。在容器中运行应用程序时限制内存和cpu绝对是个好主意――它可以阻止应用程序占用整个可用内存及/或cpu,这会导致在同一个系统上运行的其他容器毫无反应。限制资源可提高应用程序的可靠性和稳定性。它还允许为硬件容量作好规划。在kubernetes或dc/os之类的编排系统上运行容器时尤为重要。
问题
jvm可以“看到”系统上的整个内存和可用的所有cpu核心,并确保与资源一致。它默认情况下将最大堆大小(heap size)设置为系统内存的1/4,并将某些线程池大小(比如针对gc)设置为物理核心数量。不妨举例说明。
我们将运行一个简单的应用程序,它消耗尽可能多的内存(可在该网站上找到):
我们在拥有64gb内存的系统上运行,所以不妨检查默认的最大堆大小:
如上所述,它是物理内存的1/4即16gb。如果我们使用docker cgroups限制内存,会发生什么?不妨检查一下:
jvm进程被杀死了。由于它是一个子进程――容器本身幸存下来,但通常当java是容器(pid 1)内的唯一进程时,容器会崩溃。
不妨深入看看系统日志:
像这样的故障调试起来可能很难――应用程序日志中没有任何内容。在aws ecs之类的托管系统上尤其困难重重。
cpu怎么样?不妨再次检查,运行一个显示可用处理器数量的小程序:
不妨在一个cpu编号设置为1的docker容器中运行它:
不好,这个系统上的确有12个cpu。因此,即使可用处理器的数量限制为1,jvm也会尝试使用12――比如说,gc线程数量由该公式设置:
在拥有n个硬件线程(n大于8)的机器上,并行收集器使用n的固定分数作为垃圾收集器线程的数量。如果n的值很大,该分数约5/8。如果n的值低于8,使用的数字是n。
在我们的情况下:
解决方案
ok,我们现在意识到了这个问题。有解决方案吗?幸运的是,有!
新的java版本(10及以上版本)已经内置了docker支持功能。但有时升级不是办法,比如说如果应用程序与新jvm不兼容就不行。
好消息:docker支持还被向后移植到java 8。不妨检查标记为8u212的最新openjdk映像。我们将内存限制为1g,并使用1个cpu:docker run -ti --cpus 1 -m 1g openjdk:8u212-jdk。
内存:
它是256m,正好是已分配内存的1/4。
cpu:
正如我们想要的那样。
此外,还有几个新的设置:
它们允许微调堆大小――这些设置的含义在*的这个优秀答案中已得到了解释。请注意:他们设置的是百分比,而不是固定值。正因为如此,改变docker内存设置不会破坏任何东西。
如果由于某种原因不想要看到新的jvm行为,可以使用-xx:-usecontainersupport来关闭。
总结
为基于jvm的应用程序设置正确的堆大小极其重要。如果使用最新的java 8版本,你可以依赖安全(但非常保守)的默认设置。不需要在docker入口点中使用任何变通办法,也不需要再将xmx设置为固定值。
使用jvm愉快!
推荐阅读
-
AndroidStudio3 支持 Java8 了请问你敢用吗
-
AndroidStudio3 支持 Java8 了请问你敢用吗
-
Java 8 终于支持 Docker!
-
android 支持java 8 stream api(不需要minSdkVersion 24)
-
springboot启动失败:java: -source 1.5中不支持默认方法(请使用-source 8或更高版本以启用默认方法)
-
java8语法初探-编辑器报错不支持lambda表达式
-
java应用集锦8:使用poi进行excel操作,同时支持excel2003和2007
-
[Java]如何使CSV内的中文支持EXCEL格式(UTF-8 BOM)
-
Java8 传参 LocalDate,LocalDateTime,LocalTime的支持
-
如何使用vs将asp.net core项目添加容器支持并发布docker镜像到私有dockerhub和添加k8s/helm管理