赞
踩
设计并实现一个学生管理系统,即定义一个包含学生信息(学号,姓名,成绩)的顺序表,可以不考虑重名的情况,系统至少包含以下功能:
根据指定学生个数,逐个输入学生信息;
逐个显示学生表中所有学生的相关信息;
给定一个学生信息,插入到表中指定的位置;
删除指定位置的学生记录;
统计表中学生个数;
利用直接插入排序或者折半插入排序按照姓名进行排序;
利用快速排序按照学号进行排序;
根据姓名进行折半查找,成功返回此学生的学号和成绩;
文件:url80.ctfile.com/f/25127180-738851870-a46455?p=551685 (访问密码: 551685)
二、设计导向
如图所示,首先确定你要输入学生的数量,将学生的信息存储在表中,按照位置逐个输出学生的信息,删除一个位置的学生信息就是将该位置之后的元素往前移一个位置,插入一个就是将该位置之后的元素往后移一个位置;快速排序:就是选择一个元素作为中心轴,比它小的放前面,比它大的放后面;然后对其它子表也采用中心轴进行排序,直到子表只有一个元素时结束,利用了递归算法。
在我们日常的工作当中,通常应用都会采用Kubernetes进行容器化部署,但是总是会出现一些问题,例如,JVM堆小于Docker容器中设置的内存大小和Kubernetes的内存大小,但是还是会被OOMKilled。在此我们介绍一下K8s的OOMKilled的Exit Code编码。
Exit Code 137
表明容器收到了 SIGKILL 信号,进程被杀掉,对应kill -9,引发SIGKILL的是docker kill。这可以由用户或由docker守护程序来发起,手动执行:docker kill
137比较常见,如果 pod 中的limit 资源设置较小,会运行内存不足导致 OOMKilled,此时state 中的 ”OOMKilled” 值为true,你可以在系统的dmesg -T 中看到OOM日志。
为什么我设置的大小关系没有错,还会OOMKilled?
因为我的heap大小肯定是小于Docker容器以及Pod的大小的,为啥还是会出现OOMKilled?
原因分析
这种问题常发生在JDK8u131或者JDK9版本之后所出现在容器中运行JVM的问题:在大多数情况下,JVM将一般默认会采用宿主机Node节点的内存为Native VM空间(其中包含了堆空间、直接内存空间以及栈空间),而并非是是容器的空间为标准。
例如在我的机器
highlighter- code-theme-dark Dockerfile
docker run -m 100MB openjdk:8u121 java -XshowSettings:vm -version
VM settings:
Max. Heap Size (Estimated): 444.50M
Ergonomics Machine Class: server
Using VM: OpenJDK 64-Bit Server VM
以上的信息出现了矛盾,我们在运行的时候将容器内存设置为100MB,而-XshowSettings:vm打印出的JVM将最大堆大小为444M,如果按照这个内存进行分配内存的话很可能会导致节点主机在某个时候杀死我的JVM。
如何解决此问题
JVM 感知 cgroup 限制
一种方法解决 JVM 内存超限的问题,这种方法可以让JVM自动感知 docker 容器的 cgroup 限制,从而动态的调整堆内存大小。JDK8u131在JDK9中有一个很好的特性,即JVM能够检测在Docker容器中运行时有多少内存可用。为了使jvm保留根据容器规范的内存,必须设置标志-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap。
注意:如果将这两个标志与Xms和Xmx标志一起设置,那么jvm的行为将是什么?-Xmx标志将覆盖-XX:+ UseCGroupMemoryLimitForHeap标志。
总结一下
标志-XX:+ UseCGroupMemoryLimitForHeap使JVM可以检测容器中的最大堆大小。
-Xmx标志将最大堆大小设置为固定大小。
除了JVM的堆空间,还会对于非堆和jvm的东西,还会有一些额外的内存使用情况。
使用JDK9的容器感知机制尝试
highlighter- code-theme-dark Dockerfile
$ docker run -m 100MB openjdk:8u131 java
-XX:+UnlockExperimentalVMOptions
-XX:+UseCGroupMemoryLimitForHeap
-XshowSettings:vm -version
VM settings:
Max. Heap Size (Estimated): 44.50M
Ergonomics Machine Class: server
Using VM: OpenJDK 64-Bit Server VM
可以看出来通过内存感知之后,JVM能够检测到容器只有100MB,并将最大堆设置为44M。我们调整一下内存大小看看是否可以实现动态化调整和感知内存分配,如下所示。
highlighter- code-theme-dark Dockerfile
docker run -m 1GB openjdk:8u131 java
-XX:+UnlockExperimentalVMOptions
-XX:+UseCGroupMemoryLimitForHeap
-XshowSettings:vm -version
VM settings:
Max. Heap Size (Estimated): 228.00M
Ergonomics Machine Class: server
Using VM: OpenJDK 64-Bit Server VM
我们设置了容器有1GB内存分配,而JVM使用228M作为最大堆。因为容器中除了JVM之外没有其他进程在运行,所以我们还可以进一步扩大一下对于Heap堆的分配?
highlighter- code-theme-dark Dockerfile
$ docker run -m 1GB openjdk:8u131 java
-XX:+UnlockExperimentalVMOptions
-XX:+UseCGroupMemoryLimitForHeap
-XX:MaxRAMFraction=1 -XshowSettings:vm -version
VM settings:
Max. Heap Size (Estimated): 910.50M
Ergonomics Machine Class: server
Using VM: OpenJDK 64-Bit Server VM
在较低的版本的时候可以使用-XX:MaxRAMFraction参数,它告诉JVM使用可用内存/MaxRAMFract作为最大堆。使用-XX:MaxRAMFraction=1,我们将几乎所有可用内存用作最大堆。从上面的结果可以看出来内存分配已经可以达到了910.50M。
问题分析
最大堆占用总内存是否仍然会导致你的进程因为内存的其他部分(如“元空间”)而被杀死?
答案:MaxRAMFraction=1仍将为其他非堆内存留出一些空间。
但如果容器使用堆外内存,这可能会有风险,因为几乎所有的容器内存都分配给了堆。您必须将-XX:MaxRAMFraction=2设置为堆只使用50%的容器内存,或者使用Xmx。
容器内部感知CGroup资源限制
Docker1.7开始将容器cgroup信息挂载到容器中,所以应用可以从 /sys/fs/cgroup/memory/memory.limit_in_bytes 等文件获取内存、 CPU等设置,在容器的应用启动命令中根据Cgroup配置正确的资源设置 -Xmx, -XX:ParallelGCThreads等参数
在Java10中,改进了容器集成。
Java10+废除了-XX:MaxRAM参数,因为JVM将正确检测该值。在Java10中,改进了容器集成。无需添加额外的标志,JVM将使用1/4的容器内存用于堆。
java10+确实正确地识别了内存的docker限制,但您可以使用新的标志MaxRAMPercentage(例如:-XX:MaxRAMPercentage=75)而不是旧的MaxRAMFraction,以便更精确地调整堆的大小,而不是其余的(堆栈、本机…)
java10+上的UseContainerSupport选项,而且是默认启用的,不用设置。同时 UseCGroupMemoryLimitForHeap 这个就弃用了,不建议继续使用,同时还可以通过 -XX:InitialRAMPercentage、-XX:MaxRAMPercentage、-XX:MinRAMPercentage 这些参数更加细腻的控制 JVM 使用的内存比率。
Java 程序在运行时会调用外部进程、申请 Native Memory 等,所以即使是在容器中运行 Java 程序,也得预留一些内存给系统的。所以 -XX:MaxRAMPercentage 不能配置得太大。当然仍然可以使用-XX:MaxRAMFraction=1选项来压缩容器中的所有内存。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。