前言

对于那些在Java应用程序中使用Docker的CPU和内存限制的人来说,可能会遇到一些挑战。特别是CPU限制,因为JVM在内部透明地设置GC线程和JIT编译器线程的数量。

这些可以通过命令行选项 -XX:ParallelGCThreads 和 -XX:CICompilerCount 显式设置。对于内存限制,也可以通过JVM命令行选项 -Xmx 显式设置最大Java堆大小。

但是,在没有指定上述JVM命令行选项的情况下,当使用Java SE 8u121和更早版本的Java应用程序在Docker容器中运行时,可能会出现以下问题:

  • 老的 JVM 版本并不能自动的发现Docker设置的内存限制,CPU限制。这将导致JVM不能稳定服务业务,容器会杀死你JVM进程,而健康检查又将拉起你的JVM进程,进而导致一天重启次数甚至能达到几百次

首先Docker容器本质是是宿主机上的一个进程,它与宿主机共享一个/proc目录,也就是说我们在容器内看到的/proc/meminfo/proc/cpuinfo

与直接在宿主机上看到的一致,如下:

Host:

1
2
3
4
cat /proc/meminfo 
MemTotal: 197869260 kB
MemFree: 3698100 kB
MemAvailable: 62230260 kB

容器:

1
2
3
4
docker run -it --rm alpine cat /proc/meminfo
MemTotal: 197869260 kB
MemFree: 3677800 kB
MemAvailable: 62210088 kB

那么Java是如何获取到Host的内存信息的呢?没错就是通过/proc/meminfo来获取到的。

默认情况下,JVM的Max Heap Size是系统内存的1/4,假如我们系统是8G,那么JVM将的默认Heap≈2G。

Docker通过CGroups完成的是对内存的限制,而/proc目录是已只读形式挂载到容器中的,由于默认情况下Java压根就看不见CGroups的限制的内存大小,而默认使用/proc/meminfo中的信息作为内存信息进行启动,

这种不兼容情况会导致,如果容器分配的内存小于JVM的内存,JVM进程会被理解杀死。

  • 发现 “Parallel GC Threads” 和 “C* CompilerThread” 的线程数量不正常

以一个 CPU 设置为 4 的 docker 容器为例,“Parallel GC Threads” 线程数的计算公式在 vm_version.cpp 中:

  1)如果cpu核心数目少于等于8,则GC线程数量和CPU数一致

  2)如果cpu核心数大于8,则前8个核,每个核心对应一个GC线;其他核,每8个核对应5个GC线程

如果 os::active_processor_count() 返回 4,那么线程数应该是 4;但是实际的线程数为 33,可以反推 JVM 获取到的 CPU 核心数为 48,与物理机的核心数一致。

  • 使用Runtime.getRuntime().availableProcessors() ,会拿到宿主机CPU个数,而不是容器申请时的CPU个数

JDK 版本差异

  • 老的 JVM 版本(JDK 8u131以前)是无法感知容器的资源限制的。
  • 从JDK 8u131开始,在JDK 9中,JVM可以透明地了解Docker的CPU限制。

CPU 限制

  • Java SE 8u131 和 JDK9

如果没有将 -XX:paralllelgthreads 或 -XX:CICompilerCount 指定为命令行选项,JVM将应用Docker CPU限制作为JVM在系统上看到的CPU数量。

然后,JVM将调整GC线程和JIT编译器线程的数量,就像它在裸机系统上运行一样,CPU数量设置为Docker CPU限制。

如果 -XX:ParallelGCThreads 或 -XX:CICompilerCount 被指定为JVM命令行选项,并且指定了Docker CPU限制,JVM将使用 -XX:ParallelGCThreads 和 -XX:CICompilerCount 值。

只支持 --cpuset-cpus 这种指定固定 CPU 的方式:

docker run -it --cpuset-cpus="0" ubuntu /bin/bash
  • Java SE 8u191 和 JDK10

JVM知道在Docker容器中运行,并将提取特定于容器的配置信息,而不是从宿主机提取。正在提取的信息是已分配给容器的CPU数量和总内存。

Java进程可用的cpu总数是根据任何指定的cpu集、cpu共享或cpu配额计算的。此支持仅在基于Linux的平台上可用。默认情况下,此新支持是启用的,可以在命令行中使用JVM选项禁用:

-XX:-UseContainerSupport

此外,此更改还添加了一个JVM选项,该选项提供指定JVM将使用的cpu数量的能力:

-XX:ActiveProcessorCount=count

完整示例:

docker run -it --cpus=2 ubuntu /bin/bash

docker run -it --cpu-period=800000 --cpu-quota=100000 ubuntu /bin/bash

如果你对 docker 不太熟悉,可以通过官方文档理解cpus、cpu_quota、cpu_period 这三个配置项

Memory 限制

  • Java SE 8u131 和 JDK9

对于Docker内存限制,最大Java堆的透明设置还有一些工作要做。要告诉JVM在没有通过 -Xmx 设置最大Java堆的情况下注意Docker内存限制,需要两个JVM命令行选项:

-XX:+UnlockExperimentalVMOptions 和 -XX:+UseCGroupMemoryLimitForHeap

-XX:+UnlockExperimentalVMOptions 是必需的,因为在将来的版本中,目标是透明地标识Docker内存限制。

当使用这两个JVM命令行选项并且未指定 -Xmx 时,JVM将查看Linux cgroup配置,这是Docker容器用于设置内存限制的配置,以便透明地调整最大Java堆大小。

仅供参考,Docker容器也使用cGroup配置来限制CPU。

  • Java SE 8u191 和 JDK10

添加了三个新的JVM选项,以允许Docker容器用户更细粒度地控制将用于Java堆的系统内存量:

-XX:InitialRAMPercentage    #初始百分比
-XX:MaxRAMPercentage       #最大百分比
-XX:MinRAMPercentage     #最小百分比

这些选项替换已弃用的分数形式(-XX:InitialRAMFraction、-XX:maxmRamFraction和-XX:MinRAMFraction)。

总结

CPU

  • java5/6/7/8u131以前:手动设置jvm相关的选项,如:

    • ParallelGCThreads
    • ConcGCThreads
    • G1ConcRefinementThreads
    • CICompilerCount / CICompilerCountPerCPU
  • java8u131+ 和 java9+

    • java 8u131+ 和 java 8u191以前:--cpuset-cpus
    • java 8u191+: UseContainerSupport默认开启
  • java 10+:

    • 使用最新版就好了,UseContainerSupport默认开启

Memory

  • java5/6/7/8u131以前:务必设置内存选项
  • java8u131+ 和 java9+

    • java 8u131+ 和 java 8u191以前:-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap
    • java 8u191+: UseContainerSupport默认开启
  • java10+

    • 使用最新版就好了,UseContainerSupport默认开启

参考资料:

Docker——JVM 感知容器的 CPU 和 Memory 资源限制的更多相关文章

  1. [转帖]Docker容器CPU、memory资源限制

    Docker容器CPU.memory资源限制 https://www.cnblogs.com/zhuochong/p/9728383.html 处理事项内容等 这一块内容感觉 不清楚.. 背景 在使用 ...

  2. Docker(二十)-Docker容器CPU、memory资源限制

    背景 在使用 docker 运行容器时,默认的情况下,docker没有对容器进行硬件资源的限制,当一台主机上运行几百个容器,这些容器虽然互相隔离,但是底层却使用着相同的 CPU.内存和磁盘资源.如果不 ...

  3. Docker容器CPU、memory资源限制

    背景 在使用 docker 运行容器时,默认的情况下,docker没有对容器进行硬件资源的限制,当一台主机上运行几百个容器,这些容器虽然互相隔离,但是底层却使用着相同的 CPU.内存和磁盘资源.如果不 ...

  4. docker高级应用之cpu与内存资源限制(转)

    时间:2015-06-09 14:01:52      阅读:1581      评论:0      收藏:0      [点我收藏+] 标签:docker资源限制   docker cpu限制    ...

  5. jvm感知docker容器参数

    docker中的jvm检测到的是宿主机的内存信息,它无法感知容器的资源上限,这样可能会导致意外的情况. -m参数用于限制容器使用内存的大小,超过大小时会被OOMKilled. -Xmx:  默认为物理 ...

  6. 限制容器对CPU的使用 - 每天5分钟玩转 Docker 容器技术(28)

    上节学习了如何限制容器对内存的使用,本节我们来看CPU. 默认设置下,所有容器可以平等地使用 host CPU 资源并且没有限制. Docker 可以通过 -c 或 --cpu-shares 设置容器 ...

  7. 容器中JVM获取真实的CPU核数

    容器中JVM获取真实的CPU核数 基于 libsysconfcpus的方案,可以为各个版本的JDK提供一个通用的解决方案. libsysconfcpus.so的原理是截获JVM获取CPU核数所用的系统 ...

  8. Docker深入浅出系列 | 容器初体验

    目录 Docker深入浅出系列 | 容器初体验 教程目标 预备工作 容器与虚拟化技术 什么是Docker 为什么要用Docker 事例 什么是容器镜像和容器 容器与虚拟机的区别 Vagrant与Doc ...

  9. Docker背后的容器管理——Libcontainer深度解析

    Libcontainer 是Docker中用于容器管理的包,它基于Go语言实现,通过管理namespaces.cgroups.capabilities以及文件系统来进行容器控制.你可以使用Libcon ...

随机推荐

  1. [矩阵乘法]裴波拉契数列III

    [ 矩 阵 乘 法 ] 裴 波 拉 契 数 列 I I I [矩阵乘法]裴波拉契数列III [矩阵乘法]裴波拉契数列III Description 求数列f[n]=f[n-1]+f[n-2]+1的第N ...

  2. OLAP引擎:基于Presto组件进行跨数据源分析

    一.Presto概述 1.Presto简介 Presto是一个开源的分布式SQL查询引擎,适用于交互式分析查询,数据量支持GB到PB字节,Presto虽然具备解析SQL的能力,但它并不属于标准的数据库 ...

  3. 1W字|40 图|硬核 ES 实战

    前言 上篇我们讲到了 Elasticsearch 全文检索的原理<别只会搜日志了,求你懂点检索原理吧>,通过在本地搭建一套 ES 服务,以多个案例来分析了 ES 的原理以及基础使用.这次我 ...

  4. 【笔记】《Redis设计与实现》chapter8 对象

    8.1 对象的类型与编码 Redis中的每个对象都由一个redisObject结构表示,该结构中和保存数据有关的三个属性分别是type属性.encoding属性和ptr属性 typedef struc ...

  5. 自定义函数和调用函数 return返回值

    1.Q: 为什么要有函数,函数是什么? A: 函数能提高应用的模块性,和代码的重复利用率 2. 函数分为两个阶段: 1.定义阶段 2.调用阶段 3.关于函数调用: 01.函数的内存地址加上()就是调用 ...

  6. HashMap、ConcurrentHashMap 1.7和1.8对比

    本篇内容是学习的记录,可能会有所不足. 一:JDK1.7中的HashMap JDK1.7的hashMap是由数组 + 链表组成 /** 1 << 4,表示1,左移4位,变成10000,即1 ...

  7. Day07_36_Iterator迭代器

    Iterator Iterator Iterator iterator(); 获取集合所依赖的迭代对象 通过迭代器iterator()中的方法完成集合的迭代(遍历),这种方式是所有集合通用的遍历方法. ...

  8. Firefox 启动带有配置信息

    若不设置进行下述配置,那么 webdriver 每次启动火狐浏览器,默认都是一个不太有任何插件的浏览器被启动. 通过配置的方式,指定一个浏览器设置来启动,就可以使用以前安装的插件或配置信息了. 步骤一 ...

  9. input 的各种属性的验证 checkValidity兼容性

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...

  10. JEET W1S运动蓝牙耳机简评

    对于我这种喜欢运动的人来说,很早之前就一直想买个运动蓝牙耳机了.这次正好有此机会可以评测来自JEET的W1S运动蓝牙耳机,真的是非常的幸运! 终于,在期盼中,我的来自深圳的快递隔了四天终于到了!JEE ...