欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程语言 > java >内容正文

java

java对docker_Java和Docker限制问题

发布时间:2025/5/22 java 52 豆豆
生活随笔 收集整理的这篇文章主要介绍了 java对docker_Java和Docker限制问题 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

问题一:内存

时至今日,绝大多数产品级应用仍然在使用Java 8(或者更旧的版本),而这可能会带来问题。Java 8(update 131之前的版本)跟docker无法很好地一起工作。问题是在你的机器上,JVM的可用内存和CPU数量并不是Docker允许你使用的可用内存和CPU数量。

比如,如果你限制了你的Docker容器只能使用100MB内存,但是呢,旧版本的Java并不能识别这个限制。Java看不到这个限制。JVM会要求更多内存,而且远超这个限制。如果使用太多内存,Docker将采取行动并杀死容器内的进程!JAVA进程被干掉了,很明显,这并不是我们想要的。

为了解决这个问题,你需要给Java指定一个最大内存限制。在旧版本的Java(8u131之前),你需要在容器中通过设置-Xmx来限制堆大小。这感觉不太对,你可不想定义这些限制两次,也不太想在你的容器中来定义。

幸运的是我们现在有了更好的方式来解决这个问题。从Java 9之后(8u131+),JVM增加了如下标志:-XX:+UnlockExperimentalVMOptions

-XX:+UseCGroupMemoryLimitForHeap

这些标志强制JVM检查Linux的cgroup配置,Docker是通过cgroup来实现最大内存设置的。现在,如果你的应用到达了Docker设置的限制(比如500MB),JVM是可以看到这个限制的。JVM将会尝试GC操作。如果仍然超过内存限制,JVM就会做它该做的事情,抛出OutOfMemoryException。也就是说,JVM能够看到Docker的这些设置。

从Java 10之后(参考下面的测试),这些体验标志位是默认开启的,也可以使用-XX:+UseContainerSupport来使能(你可以通过设置-XX:-UseContainerSupport来禁止这些行为)。

问题二:CPU

第二个问题是类似的,但它与CPU有关。简而言之,JVM将查看硬件并检测CPU的数量。它会优化你的runtime以使用这些CPUs。但是同样的情况,这里还有另一个不匹配,Docker可能不允许你使用所有这些CPUs。可惜的是,这在Java 8或Java 9中并没有修复,但是在Java 10中得到了解决。

从Java 10开始,可用的CPUs的计算将采用以不同的方式(默认情况下)解决此问题(同样是通过UseContainerSupport)。

Java和Docker的内存处理测试

作为一个有趣的练习,让我们验证并测试Docker如何使用几个不同的JVM版本/标志甚至不同的JVM来处理内存不足。

首先,我们创建一个测试应用程序,它只是简单地“吃”内存并且不释放它。javaimport 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() );

}

}

}

我们可以启动Docker容器并运行这个应用程序来查看会发生什么。

测试一:Java 8u111

首先,我们将从具有旧版本Java 8的容器开始(update 111)。docker run -m 100m -it java:openjdk-8u111 /bin/bash

我们编译并运行MemEat.java文件:javac MemEat.java

java MemEat...

free memory: 67194416

free memory: 66145824

free memory: 65097232

Killed

正如所料,Docker已经杀死了我们的Java进程。不是我们想要的(!)。你也可以看到输出,Java认为它仍然有大量的内存需要分配。

我们可以通过使用-Xmx标志为Java提供最大内存来解决此问题:javac MemEat.java

java -Xmx100m MemEat...

free memory: 1155664

free memory: 1679936

free memory: 2204208

free memory: 1315752

Exception in thread "main"

java.lang.OutOfMemoryError: Java heap space

at MemEat.main(MemEat.java:8)

在提供了我们自己的内存限制之后,进程正常停止,JVM理解它正在运行的限制。然而,问题在于你现在将这些内存限制设置了两次,Docker一次,JVM一次。

测试二:OpenJ9

我最近也在试用OpenJ9,这个免费的替代JVM已经从IBM J9开源,现在由Eclipse维护。 它运行速度快,内存管理非常好,性能卓越,经常可以为我们的微服务节省多达30-50%的内存。这几乎可以将Spring Boot应用程序定义为’micro’了,其运行时间只有100-200mb,而不是300mb+。我打算尽快就此写一篇关于这方面的文章。 但令我惊讶的是,OpenJ9还没有类似于Java 8/9/10+中针对cgroup内存限制的标志(backported)的选项。如果我们将以前的测试用例应用到最新的AdoptAJDK OpenJDK 9 + OpenJ9 build:docker run -m 100m -it adoptopenjdk/openjdk9-openj9 /bin/bash

我们添加OpenJDK标志(OpenJ9会忽略的标志):java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap

MemEat...

free memory: 83988984

free memory: 82940400

free memory: 81891816

Killed

Oops,JVM再次被Docker杀死。 我真的希望类似的选项将很快添加到OpenJ9中,因为我希望在生产环境中运行这个选项,而不必指定最大内存两次。 Eclipse/IBM正在努力修复这个问题,已经提了issues,甚至已经针对issues提交了PR。

测试三:OpenJ9(Nightly)

有人建议使用OpenJ9的最新nightly版本。docker run -m 100m -it adoptopenjdk/openjdk9-openj9:nightly /bin/bash

最新的OpenJ9夜间版本,它有两个东西: 另一个有问题的PATH参数,需要先解决这个问题 JVM支持新标志UseContainerSupport(就像Java 10一样)export PATH=$PATH:/opt/java/openjdk/jdk-9.0.4+12/bin/javac MemEat.java

java -XX:+UseContainerSupport

MemEat...

free memory: 5864464

free memory: 4815880

free memory: 3443712

free memory: 2391032

JVMDUMP039I Processing dump event "systhrow", detail "java/lang/OutOfMemoryError"

at 2018/05/15 21:32:07 - please wait.JVMDUMP032I

JVM requested System dump using '//core.20180515.213207.62.0001.dmp' in response to an eventJVMDUMP010I

System dump written to //core.20180515.213207.62.0001.dmpJVMDUMP032I

JVM requested Heap dump using '//heapdump.20180515.213207.62.0002.phd' in response to an eventJVMDUMP010I

Heap dump written to //heapdump.20180515.213207.62.0002.phdJVMDUMP032I

JVM requested Java dump using '//javacore.20180515.213207.62.0003.txt' in response to an eventJVMDUMP010I

Java dump written to //javacore.20180515.213207.62.0003.txtJVMDUMP032I

JVM requested Snap dump using '//Snap.20180515.213207.62.0004.trc' in response to an eventJVMDUMP010I

Snap dump written to //Snap.20180515.213207.62.0004.trcJVMDUMP013I

Processed dump event "systhrow", detail "java/lang/OutOfMemoryError".

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

TADAAA,正在修复中! 奇怪的是,这个标志在OpenJ9中默认没有启用,就像它在Java 10中一样。再说一次:确保你测试了这是你想在一个Docker容器中运行Java。 结论 简言之:注意资源限制的不匹配。测试你的内存设置和JVM标志,不要假设任何东西。 如果您在Docker容器中运行Java,请确保你设置了Docker内存限制和在JVM中也做了限制,或者你的JVM能够理解这些限制。 如果您无法升级您的Java版本,请使用-Xmx设置您自己的限制。 对于Java 8和Java 9,请更新到最新版本并使用:-XX:+UnlockExperimentalVMOptions

-XX:+UseCGroupMemoryLimitForHeap

对于Java 10,确保它支持’UseContainerSupport’(更新到最新版本)。

对于OpenJ9(我强烈建议使用,可以在生产环境中有效减少内存占用量),现在使用-Xmx设置限制,但很快会出现一个支持UseContainerSupport标志的版本。

总结

以上是生活随笔为你收集整理的java对docker_Java和Docker限制问题的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。