JVM 如何获取当前容器的资源限制?

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
可观测可视化 Grafana 版,10个用户账号 1个月
简介: 本文是《容器中的 Java》系列文章之 1/n,欢迎关注后续连载 :) 。

作者:卜比


本文是《容器中的 Java》系列文章之 1/n,欢迎关注后续连载 :) 。


最近同事说到Java的 ParallelGCThreads[1] 参数,我翻了下 jdk8 的代码,发现 ParallelGCThreads 的参数默认值如下:


  • 如果 CPU 核心数目少于等于 8,则 GC 线程数量和CPU数一致
  • 如果 CPU 核心数大于 8,则前 8 个核,每个核心对应一个 GC 线;其他核,每 8 个核对应 5 个 GC 线程


但是被提醒,发现即使在分配 4 核的容器上,GC 线程数也为 38。然后就想到应该和容器的资源限制有关——jvm 可能无法觉察到当前容器的资源限制。


翻了下代码,发现最新版本的 Java 是能感知容器的资源限制的,就按照 jdk 版本再翻了下代码:


线上的 jdk(jdk8u144)


写一个 sleep 1000s 的程序,用于查看 JVM 的线程数量:


./jdk1.8.0_144/bin/java -XX:+UseG1GC -XX:+ParallelRefProcEnabled Main


然后查看 GC 线程数目:


$ jstack $pid | grep 'Parallel GC Threads' | wc -l38


一算就知道物理机器有 56 个核心(8+(56-8)*5/8=38)
然后使用 +PrintFlagsFinal 看下参数:


$ ./jdk1.8.0_144/bin/java -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal  -version | grep ParallelGCThreads
uintx ParallelGCThreads          =38                     {product}


看来 jdk8u144 并无法读取容器配额。


jdk 8u191


然后发现,从 jdk 8u191[2] 版本开始,Java 就可以读取容器配额了:
运行同样的程序:


./jre1.8.0_191/bin/java -XX:+UseG1GC -XX:+ParallelRefProcEnabled Main
$ jstack $pid | grep 'Parallel GC Threads' | wc -l
4


查看实际参数:


$ ./jre1.8.0_191/bin/java -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal  -version | grep ParallelGCThreads
uintx ParallelGCThreads         =4                   {product}


另外,jdk 8u191 引入了 PrintContainerInfo 参数:


$ ./jre1.8.0_191/bin/java -XX:+UnlockDiagnosticVMOptions -XX:+PrintContainerInfo -version
OSContainer::init: Initializing Container Support
Path to /memory.limit_in_bytes is /sys/fs/cgroup/memory/memory.limit_in_bytes
Memory Limit is: 10737418240
Path to /cpu.cfs_quota_us is /sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us
CPU Quota is: 400000
Path to /cpu.cfs_period_us is /sys/fs/cgroup/cpu,cpuacct/cpu.cfs_period_us
CPU Period is: 100000
Path to /cpu.shares is /sys/fs/cgroup/cpu,cpuacct/cpu.shares
CPU Shares is: 681
CPU Quota count based on quota/period: 4
CPU Share count based on shares: 1
OSContainer::active_processor_count: 4
Path to /cpu.cfs_quota_us is /sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us
CPU Quota is: 400000
Path to /cpu.cfs_period_us is /sys/fs/cgroup/cpu,cpuacct/cpu.cfs_period_us
CPU Period is: 100000
Path to /cpu.shares is /sys/fs/cgroup/cpu,cpuacct/cpu.shares
CPU Shares is: 681
CPU Quota count based on quota/period: 4
CPU Share count based on shares: 1
OSContainer::active_processor_count: 4
……
java version "1.8.0_191"
Java(TM) SE Runtime Environment (build 1.8.0_191-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode)


可以看到,获取的内存限制、可用 CPU 数目都是对的了。


如何获取容器资源配额呢?


结合这个日志和代码,我们也可以看到如何获取容器配额:


首先从 /proc/self/mounts 中读取对应的资源的 mount 位置,比如 CPU 就是在 /sys/fs/cgroup/cpu,cpuacct 


$ cat /proc/mounts  | grep -E -w '(cpu|memory)'
cgroup /sys/fs/cgroup/memory cgroup ro,nosuid,nodev,noexec,relatime,memory 0 0
cgroup /sys/fs/cgroup/cpu,cpuacct cgroup ro,nosuid,nodev,noexec,relatime,cpu,cpuacct 0 0


对于内存:


$ cat /sys/fs/cgroup/memory/memory.limit_in_bytes
10737418240


对于 CPU 资源:


其一,可以通过 quota/period 来算:


$ cat /sys/fs/cgroup/cpu,cpuacct/cpu.cfs_period_us #单CPU总时间片配额,微秒 
100000
$ cat /sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us #时间片内,容器可占用的CPU时间
400000


比如上面就表示分配了 4 核。


也可以通过 cpu.shares 来获取:


$ cat /sys/fs/cgroup/cpu,cpuacct/cpu.shares
681


不过这个值是 CPU 占用份额,无法根据这个算出来可用 CPU 数量,所以基本没用…


总结


使用 jdk 8u191 或更新版本就完事了。


相关链接


[1] ParallelGCThreads:

https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/parallel.html


[2] jdk 8u191:

https://www.oracle.com/java/technologies/javase/8u191-relnotes.html#JDK-8146115

相关文章
|
4月前
|
Serverless API 容器
函数计算容器模式如何设置多久释放资源啊?
函数计算容器模式如何设置多久释放资源啊?
367 0
|
7月前
|
弹性计算 缓存 Kubernetes
容器&服务:开篇,压力与资源
本文是服务&容器系列的开篇,以需要解决的核心问题作为铺垫。在后续篇章中,将逐步开始对相关技术进行深入研究,并解决典型的问题作为实践,从中发现使用中可能遇到的问题并解决,深入掌握。
39 0
|
6月前
|
Cloud Native Linux 虚拟化
【云原生】Docker容器资源限制(CPU/内存/磁盘)
【云原生】Docker容器资源限制(CPU/内存/磁盘)
748 0
|
6月前
|
弹性计算 Kubernetes 调度
《Docker资源限制和调度策略:性能优化与资源管理,打造高效稳定的容器环境》
《Docker资源限制和调度策略:性能优化与资源管理,打造高效稳定的容器环境》
147 0
|
6月前
|
Go 虚拟化 云计算
Docker 基础知识解析:容器与传统虚拟化对比:资源利用、启动时间、隔离性和部署效率
Docker 基础知识解析:容器与传统虚拟化对比:资源利用、启动时间、隔离性和部署效率
179 0
|
2月前
|
Kubernetes Linux 调度
「译文」深入了解 Kubernetes 指标 - 第三部分 - 容器资源指标
「译文」深入了解 Kubernetes 指标 - 第三部分 - 容器资源指标
|
9月前
|
运维 Cloud Native Java
不得不提的容器 JVM
当我们将 JVM 生态中的关键要素,例如,垃圾收集器、堆大小和运行时编译器设置默认值时,许多技术人员(开发、运维人员)或许应该意识到在 Linux 容器生态中(诸如,Docker、Rkt、RunC、Lxcfs 等)内所运行的 Java 进程的实际行为与预期不符。当我们在没有任何调优参数(例如,最为简洁的的启动命令行:“ java -jar myapplication .jar”)的情况下执行 Java 应用程序时,JVM 将自行调整某些特定的参数,以在当前执行环境中获得最佳性能表现。
107 0
|
7月前
|
Cloud Native Serverless API
云原生容器Clouder认证:容器应用与集群管理—课时6:创建集群和准备资源
云原生容器Clouder认证:容器应用与集群管理—课时6:创建集群和准备资源
109 0
|
9月前
|
虚拟化 Docker 容器
容器的资源限制实战
vmvare 安装的虚拟机可以通过界面设定cpu、内存... 的资源限制,如下图。docer 容器也同样可以通过指定参数来达到这个目的。
62 0
|
5天前
|
存储 运维 监控
构建高效稳定的Docker容器监控体系
【4月更文挑战第18天】 在现代微服务架构中,Docker容器已成为部署和运行应用的标准环境。随之而来的挑战是如何有效监控这些容器的性能与健康状况,确保系统的稳定性和可靠性。本文将探讨构建一个高效稳定的Docker容器监控体系的关键技术和方法,包括日志管理、性能指标收集以及异常检测机制,旨在为运维人员提供实用的指导和建议。
10 0