首页 > 系统相关 >【docker】Java应用 容器内存管理 -XX:+UseContainerSupport

【docker】Java应用 容器内存管理 -XX:+UseContainerSupport

时间:2022-11-11 12:34:32浏览次数:42  
标签:容器 Java XX 内存 Jvm docker UseContainerSupport


早期时候,容器内运行Java应用程序时,Jvm无法感知容器环境存在,所以对容器资源的限制比如内存或者cpu等都无法生效。原因是容器的资源管理使用了操作系统cgroup机制,但是Jvm无法感知cgroup。所以可能需要在jvm以及docker中指定两次内存限制。后来,在Jvm9及以后,Jvm开始了对容器资源限制的支持。在Jvm11中,可以使用-XX:+UseContainerSupport参数来制定Jvm使用容器内存。另外还有两个参数-XX:InitialRAMPercentage -XX:MaxRAMPercentage来制定Jvm使用容器内存的百分比。这里就以这三个参数为例,写一个简单的demo。

Java应用使用spring-boot:

@RequestMapping(value = "/hello")
public String hello() {
List<byte[]> list = new ArrayList<>();
for (int i = 0; i < 50; i++) {
list.add(new byte[1024 * 1024 * 200]);
System.out.println("use " + 200 * (i + 1) + " M memory");
}
return "hello";
}

这里只给出http接口的简单定义,循环分配200M的内存。

DockerFile:

FROM centos:latest

ADD target/test-maven-api.jar /app.jar
ADD ./jdk-11.0.1 /var/local/jdk11
ENV JAVA_HOME /var/local/jdk11/bin
ENV PATH=$PATH:$JAVA_HOME
EXPOSE 8080
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:InitialRAMPercentage=50 -XX:MaxRAMPercentage=80"
ENTRYPOINT java ${JAVA_OPTS} -jar /app.jar

因为对docker不太熟,简单写了一个DockerFile。使用centos作为基础镜像,然后将本地的jdk11导入到容器中。本来想直接基于jdk的基础镜像来做,但是仅有jdk9基础镜像,不知道是不是因为openjdk的原因。其次就是Java环境变量的设置以及定义了Java启动命令。这里定义了该Java应用的堆内存只能使用容器内50%-80%的内存。使用如下命令创建镜像并运行容器,限制了容器内存为1G:

docker build --rm -f "Dockerfile" -t java-springboot:latest .
docker run -m 1G -d -p 8080:8080 java-springboot

使用docker ps确认容器启动成功:

【docker】Java应用 容器内存管理 -XX:+UseContainerSupport_内存

然后访问浏览器地址,并使用如下命令查看程序日志:

docker logs -f -t --tail 500 dd272

【docker】Java应用 容器内存管理 -XX:+UseContainerSupport_内存_02

从这里可以看到,在申请800M内存时Jvm抛出OutOfMemory异常,这与预期相符,因为容器最大内存限制为1G,Jvm堆内存最大使用80%,也就是800M,所以申请800M内存失败了(Jvm本身及程序其他部分也会消耗一定的堆内存)。说明-XX:+UseContainerSupport参数确实生效了。

下面再看看不使用-XX:+UseContainerSupport的情况。我们仅需要替换之前DockerFile里的JAVA_OPTS参数即可:

ENV JAVA_OPTS="-Xms4g -Xmx4g"

看下日志:

【docker】Java应用 容器内存管理 -XX:+UseContainerSupport_内存_03

这里申请的内存量超过了容器1G内存的限制,说明不使用-XX:+UseContainerSupport参数时Jvm无法感知容器内存限制的存在。并且由于超过容器内存限制,Jvm进程被容器kill了。至于为什么是1.6G时被kill了,原因还没有深究,应该与容器内存分配策略有关。

标签:容器,Java,XX,内存,Jvm,docker,UseContainerSupport
From: https://blog.51cto.com/u_15873544/5844592

相关文章

  • 【Java】Instrumentation热更新 premain agentmain
    有两种办法:1)在java5中,可以利用jvm加载类的一个扩展点来实现类文件的动态修改。需要提供一个premain方法。缺点是只能在类文件加载且main方法执行之前修改,无法实现真正的运行......
  • 【zookeeper】java API 例子
    之前体验了命令行客户端,这次看一下javaAPI操作zk。server还是按照之前的配置,一个server1,server2和server3的伪集群。maven:这里使用maven管理zk的jar包,大致需要zk的jar和日......
  • 狂神说Javase基础学习1
    狂神学习博客1基本的DOS命令打开CMD的方式1.开始+系统+命令提示符2.win+R,进入运行窗口,输入cmd打开控制台3.在任意的文件夹下面,Shift+鼠标右键,进入命令行窗口4.资源管......
  • 【Java】内存模型 volatile
    java堆存储对象和数组,是一块线程共享数据区,但是实际线程运行的时候,对于用到的对象都会在线程私有空间即虚拟机栈保存一个副本,为了效率。这两快内存叫主内存和工作内存。java......
  • 【Java】内存区域与对象创建
    这块内容是java很基础的部分,涉及到JVM的设计原理,很久以前就看到过,这次需要区分线程私有和共享基本java的运行时数据区可以分为五大块:程序计数器,为线程私有,每一个线程都有一......
  • 【Java】split(
    java的split函数接受一个正则表达式的分隔符为参数,将string按照分隔符划分为一个数组。我们可能会忽略这个参数的要求,这里传入的分隔符并不是一个普通的字符串,而是一个正则......
  • 【Java】多线程 数目
    今天看到一篇文章,讲多线程数目的,很棒这个问题还是很容易被忽略的,就是多线程到底是为了什么?最开始学习多线程的时候,往往将多线程和性能高划等号,只要用了多线程就能提升性能,其......
  • 【Java】NoSuchMethodError
    刚开始写代码时,特别是类似web这种需要很多第三方jar包的项目,经常会遇到这个问题。这次记录下这个报错的原因。简而言之,这个报错是肯定是因为compile时方法存在,但是runtime时......
  • 【Java】反射与单例
    双重检验与静态内部类两种方法都可以实现延迟加载的单例模式。但是无法阻止反射破坏单例,因为反射可以无视修饰权限,直接调用构造方法创建对象,下面是一个例子:packageThreadTe......
  • 【Java】序列化与单例
    之前明白了线程安全且延迟加载的单例如何写,有两种,双重检验和静态内部类。然后为了防止反射破坏单例,在私有构造方法里面加入了一个同步变量的判断,确保构造方法只调用一次。......