2019-04-297279
版权
本文涉及的产品
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
推荐场景:
立即试用
容器镜像服务 ACR,镜像仓库100个 不限时长
推荐场景:
立即试用
 
简介: 背景 在k8s docker环境中执行Java程序,因为我们设置了cpu,memory的limit,所以Java程序执行时JVM的参数没有跟我们设置的参数关联,导致JVM感知到的cpu和memory是我们k8s的work node上的cpu和memory大小。

背景

在k8s docker环境中执行Java程序,因为我们设置了cpu,memory的limit,所以Java程序执行时JVM的参数没有跟我们设置的参数关联,导致JVM感知到的cpu和memory是我们k8s的work node上的cpu和memory大小。这样造成的问题是:当容器中Java程序使用内存超过memory limit时,直接造成Out of Memory错误,从而引起容器重启。JVM很多参数也是很智能的,启动时内存的分配也会根据cpu和memory进行调整,比如GC相关的参数就是动态调整的。如果容器感知到的cpu核数不对,那么对程序的性能也会造成很大的影响。

内存

Java对内存的使用有几个参数可以配置。以前的版本可以用-Xms, -Xmx来分别设置初始化Java堆大小和最大的Java堆大小。但因为Java堆大小并不等于所有可用的内存大小,所以在设置memory limit的时候会加一个值。这样避免Java使用的内存超过分配给容器的最大内存限制。这个增加的值需要一定的经验和测试来获取。

JVM后来提供了UseCGroupMemoryLimitForHeap参数来让JVM自动根据我们提供的内存限制来分配堆的大小。这样也就避免了我们人为去确定应该给堆多大的空间。只要经过测试,确定这个Java程序占用的总共空间就行了。使用方法是在java运行后面加上参数:java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap ⋯

CPU

配了上面的参数,我们还没有完全解决问题。因为JVM GC相关的参数跟CPU处理器核相关联的,可使用的CPU核数越多,分配给GC的线程资源也越多。如果我们不设置正确的CPU核数给容器,那么它看到的就是整个k8s worker node的CPU个数,比如我们限制容器可使用2core,但worker node有32core。那么这个容器会给GC分配很多的线程资源,从而严重影响正常Java线程的运行。

CPU个数对JVM GC的影响

JVM提供了ActiveProcessorCount参数来设置这个值。但这个参数只在java 1.8.0_191以后版本才支持。下面我在笔记本上做了测试(total 8 cores),看看这个参数如何影响GC的参数。

Step1: 写一个hello wold程序。

root@kyle:~# cat Hello.java
public class Hello{
public static void main(String[] args){
System.out.println("hello world");
}

Step2: 编译

root@kyle:~# javac Hello.java

Step3: 不加参数运行

root@kyle:~# java -XX:+PrintFlagsFinal Hello > init.txt
[Global flags]
intx ActiveProcessorCount = -1 {product}
uintx AdaptiveSizeDecrementScaleFactor = 4 {product}
uintx AdaptiveSizeMajorGCDecayTimeScale = 10 {product}
uintx AdaptiveSizePausePolicy = 0 {product}
uintx AdaptiveSizePolicyCollectionCostMargin = 50 {product}

Step4: 加不同参数值运行

root@kyle:~# java -XX:ActiveProcessorCount=1 -XX:+PrintFlagsFinal Hello > p1.txt
root@kyle:~# java -XX:ActiveProcessorCount=2 -XX:+PrintFlagsFinal Hello > p2.txt
root@kyle:~# java -XX:ActiveProcessorCount=4 -XX:+PrintFlagsFinal Hello > p4.txt
root@kyle:~# java -XX:ActiveProcessorCount=8 -XX:+PrintFlagsFinal Hello > p8.txt

Step5: 看看不同参数对GC的影响:
1个处理器跟2个处理器的比较:

 root@kyle:~# diff p1.txt p2.txt
2c2
< intx ActiveProcessorCount := 1 {product}
---
> intx ActiveProcessorCount := 2 {product}
304c304
< uintx MarkSweepDeadRatio = 5 {product}
---
> uintx MarkSweepDeadRatio = 1 {product}
311c311
< uintx MaxHeapFreeRatio = 70 {manageable}
---
> uintx MaxHeapFreeRatio = 100 {manageable}
335,336c335,336
< uintx MinHeapDeltaBytes := 196608 {product}
< uintx MinHeapFreeRatio = 40 {manageable}
---
> uintx MinHeapDeltaBytes := 524288 {product}
> uintx MinHeapFreeRatio = 0 {manageable}
388c388
< uintx ParallelGCThreads = 0 {product}
---
> uintx ParallelGCThreads = 2 {product}
682,683c682,683
< bool UseParallelGC = false {product}
< bool UseParallelOldGC = false {product}
---
> bool UseParallelGC := true {product}
> bool UseParallelOldGC = true {product}

2个处理器跟4个处理器的比较:

root@kyle:~# diff p2.txt p4.txt
2c2
< intx ActiveProcessorCount := 2 {product}
---
> intx ActiveProcessorCount := 4 {product}
59c59
< intx CICompilerCount := 2 {product}
---
> intx CICompilerCount := 3 {product}
388c388
< uintx ParallelGCThreads = 2 {product}
---
> uintx ParallelGCThreads = 4 {product}

4个处理器跟8个处理器的比较:

root@kyle:~# diff p4.txt p8.txt
2c2
< intx ActiveProcessorCount := 4 {product}
---
> intx ActiveProcessorCount := 8 {product}
59c59
< intx CICompilerCount := 3 {product}
---
> intx CICompilerCount := 4 {product}
388c388
< uintx ParallelGCThreads = 4 {product}
---
> uintx ParallelGCThreads = 8 {product}

不加参数跟8个处理器的比较:

root@kyle:~# diff init.txt p8.txt
2c2
< intx ActiveProcessorCount = -1 {product}
---
> intx ActiveProcessorCount := 8 {product}

从上面比较可以看出,不设这个参数跟设置最大参数(当前系统是8core)是一样的。2,4,8核设置只影响ParallelGCThreads, CICompilerCount。但如果只用1核的话,UseParallelGC,UseParallelOldGC都变为false,同时也会影响其它几个参数。见上面diff p1.txt p2.txt比较结果。

CPU个数对Java程序的影响

CPU个数的设置除了对JVM GC性能产生影响外,对Java的工作线程也会产生影响。以下的代码常用于Java库,它会根据CPU的个数产生工作线程。如果没有正确设置docker中的参数,对实际的程序性能会产生很大的影响。

Runtime.getRuntime().availableProcessors()

以下代码摘自aliyun-log-java-producer库,是根据可用处理器来产生相应个数的IO线程来发送loghub数据。

# ProducerConfig.java:
public class ProducerConfig {
public static final int DEFAULT_IO_THREAD_COUNT =
Math.max(Runtime.getRuntime().availableProcessors(), 1);

OpenJDK版本

我们运行以下命令检查JDK的版本。openjdk version "1.8.0_131"以后支持UseCGroupMemoryLimitForHeap参数,"1.8.0_191"以后才支持ActiveProcessorCount这个参数。

root@kyle:~# java -version
openjdk version "1.8.0_191"
OpenJDK Runtime Environment (build 1.8.0_191-8u191-b12-2ubuntu0.16.04.1-b12)
OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)

改进方案

如果我们使用的JDK版本支持这2个参数,那么我们只需要在运行Java程序时把这UseCGroupMemoryLimitForHeap参数加上,同时再给ActiveProcessorCount参数赋值实际分配给容器的cpu limit就可以了。如果目前的JDK版本低于1.8.0_191,即不支持ActiveProcessorCount,针对这个情况,有2种方法可以进行:

  1. 建议升级到191以后的版本,然后根据cpu limit配置ActiveProcessorCount。
  2. 不升级jdk版本,直接设置跟ActiveProcessorCount参数相关的GC参数:比如ParallelGCThreads,CICompilerCount。如果是1.8.0_131以前的版本,可以用-Xms, -Xmx参数进行堆空间的大小分配,注意这两个参数只设置了分配给堆的大小,实际的memory limit应该比这个大。这种方案不是一个best practice,毕竟这样没有用到JVM自动适配的一些参数。最关键的,此种方法不能避免很多Java库根据availableProcessors()来做相应逻辑处理。

参考资料

  1. Assign Memory Resources to Containers and Pods
  2. Assign CPU Resources to Containers and Pods
  3. Kubernetes Demystified: Restrictions on Java Application Resources
  4. JVM 对 docker 容器 CPU 限制的兼容
  5. Java SE support for Docker CPU and memory limits
  6. 关于Jvm知识看这一篇就够了

[转帖]Java程序在K8S容器部署CPU和Memory资源限制相关设置的更多相关文章

  1. Docker——JVM 感知容器的 CPU 和 Memory 资源限制

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

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

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

  3. 从安装linux(centos7.6)系统到部署springboot java程序到k8s(大纲)

    本文说明从安装linux系统开始,一直到在k8s运行springboot程序全过程 本文假设在自己电脑操作,因此linux系统使用vmware虚拟机,linux发行版使用centos 7.6.1810 ...

  4. java程序故障排查脚本之——CPU占用高

    root@ubuntu-B85M-D3H:~/tmp# cat java_Analy.sh #!/bin/bash T=`ps -mp $1 -o THREAD,tid,time|sort -k 2 ...

  5. Java 程序员生产神器 IDEA 的常用快捷键、插件及设置

    对于 Java 程序员来说,使用 IDEA 集成环境是最称手的.优点不多讲,用过的人都知道.IDEA 虽好,但为了充分利用 IDEA 的优势,我分享一下我常用快捷键.插件和设置. 常用快捷键 Ctrl ...

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

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

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

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

  8. java程序监控tomcat中部署的项目的状态以及控制某些项目的启动停止

    原文:http://blog.csdn.net/liuyuqin1991/article/details/49280777 步骤如下: ①:首先授权用户使获得这些权限 You can find the ...

  9. java web项目war包部署,使用tomcat对指定接口设置身份认证

    先简单说一下需求: 将一个基于springboot2.0开发的java web项目打成war包,通过tomcat部署到一台linux服务器上,项目相关的一些图片等资源也按照一定规则放置在服务器构建好的 ...

  10. java程序练习:输入数字转换成中文输出(金额相关)

    //题目,做一个输入金额数字,输出转换成中文的金额名称.public class Test { public static void main(String[] args) { System.out. ...

随机推荐

  1. Java的特性、内容和环境的配置

    Java的特性和优势 简单性 面向对象 可移植性 高性能 分布式 动态性 多线程 安全性 健壮性 JDK包含JRE包含JVM JDK:Java Development Kit JRE:Java Run ...

  2. Redis 打怪升级进阶成神之路(2023 最新版)!

    前面我们学习:MySQL 打怪升级进阶成神之路(2023 最新版)!,然后我们就开始了 NoSQL 卷王之路.从第一篇文章开始,我们逐步详细介绍了 Redis 基础理论与安装配置.9 种数据类型和应用 ...

  3. JavaFx FXML入门(五)

    JavaFx FXML入门(五) JavaFX 从入门入门到入土系列 JavaFx的FXML类似安卓中的视图文件,可以添加样式,添加css,添加id然后在java代码中绑定点击事件.可以使用工具编辑: ...

  4. 【好书推荐】《Python黑魔法指南》-附高清PDF版

    摘要:<Python 黑魔法手册.pdf >作者(明哥)是一个从事云计算多年的 Python 重度用户,它把自已多年的 Python 编码经验整理成小册子,没有长篇大论,半天就能全能掌握, ...

  5. 实时入库不用愁,HStore帮分忧

    本文分享自华为云社区<直播回顾 | 实时入库不用愁,HStore帮分忧>,作者:汀丶. 海量数据时代,如何实现数据实时入库与实时查询?GaussDB(DWS) HStore表为数据高效存储 ...

  6. MES/MOM国内市场现状趋势与新生态模式参考

    本文分享自华为云社区<工业互联网系列(七)MES/MOM国内市场现状趋势与新生态模式参考>,作者:云起MAE . 国内工业互联网平台服务整体围绕数字化及数据价值挖掘的底层逻辑没有变,变的是 ...

  7. OUT了吧,Kafka能实现消息延时了

    摘要:本文讲述如何在保存Kafka特有能力的情况下给Kafka扩充一个具有能处理延时消息场景的能力. 本文分享自华为云社区<Kafka也能实现消息延时了?>,作者:HuaweiCloudD ...

  8. 华为AppCube通过中国信通院“低代码开发平台通用能力要求”评估!

    摘要:华为AppCube应用魔方顺利通过信通院评估,被认证为具备 "低代码开发平台通用能力"的企业服务平台. 本文分享自华为云社区<华为AppCube通过中国信通院" ...

  9. 资源成本降低60%!火山引擎ByteHouse助力数字营销平台仟传网络降本增效

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群   近日,中国知名内容社交平台整合营销企业仟传网络与火山引擎ByteHouse达成合作.仟传网络将通过火山引擎By ...

  10. C# Winform 自定义窗口,最大化遮住任务栏

    解决 C# Winform 自定义窗口,最大化遮住任务栏 的问题,可以通过获取屏幕大小来控制最大值,来实现,代码如下 Rectangle ScreenArea = System.Windows.For ...