内存管理及调优
32 位系统最大内存寻址空间为2^32=4GiB,64 位系统原则上最大内存寻址空间为2^64=64EiB,几乎没有内存空间限制,然后根据操作系统内核版本的不同,在RHEL 8 中,最大内存寻址空间是8TiB,其他64 位操作系统版本可能有支持最大寻址空间是256TiB 的,目前 257TiB - 16EiB 这段空间预留未开发。
ps aux 命令显示的列中,有两个参数,一个是VSS,表示进程申请的虚拟内存空间大小, 一个是RSS,表示实际为进程分配到的内存空间大小,原则上,如果不对进程做限制,它可以申请到系统所有的内存空间。
RHEL 8 内核支持最多申请8TiB 物理内存,最多256TiB 虚拟内存。
内存地址的分配是由CPU 的MMU(内存管理单元)管理和分配的,管理物理内存到虚拟内存的映射。
虚拟内存是线性的连续的地址空间,而物理内存空间可能并不连续,它是按需分配的。
打开一个火狐浏览器:
查看火狐浏览器申请了多少内存:
这里看到申请的内存差不多是3G,这并不是物理内存空间,而是虚拟内存空间,物理内存空间是按需分配的。
total 这一行的上面,每一行是打开火狐浏览器这个进程所需要调用的文件,每一个文件所需要消耗的内存空间。
实际分配了大约250M 内存空间。
仔细观察上面pmap 命令显示的结果会发现,每一个被调用的文件所占用的内存空间大小都是4 的整数倍,这是因为内存是有最小分配单元的,叫做PageSize(页大小),在x86 架构的系统上,默认是4KiB,在ARM 架构的处理器上,可能是64KiB
buff/cache 是用来提升读性能的,脏页是用来提升写性能的。
vm.dirty_expire_centisecs 是脏页回收时间,单位是百分之一秒,3000就是30 秒,每隔30秒回收一次脏页。
vm.dirty_ratio 是脏页回收比例,很多中文翻译也叫水位值,脏数据大小超过内存剩余空间30% 开始回收脏页;
vm.dirty_writeback_centisecs 是脏页回收的检测时间,单位也是百分之一秒,500 就是5秒,每隔5秒检测一次脏页是否需要回收。
vm.dirty_bytes 是可以指定具体脏页数据达到多大空间开始回收,这里等于0 表示不关心这个参数。
vm.dirty_backgroud_ratio 这里等于10,表示某一个程序的脏数据达到内存剩余可用空间的10% 开始回收。
vm.dirty_backgroud_bytes 可以指定某一个程序脏数据具体达到多大空间开始回收,这里等于0 表示不关心这个参数。
调整到某个程序脏数据达到内存空间的5%就开始回收。
这个命令会生成大量的随机数,写入/dev/vdc 设备,执行过程中特别消耗内存脏页。
现在执行这条命令的主机内存是4G,现在来观察脏数据大小,应该到了10% (大约200M,实际肯定小于200)就会开始回收:
差不多到170M 就开始回收了。
当内存紧张时,更倾向于释放cache,还是倾向于保留cache(使用swap),读IO 很随机时,建议尽量保留cache
取值范围是0-100,默认是60,这是被调优过的。
如果设置为0,表示在内存不足时更倾向于清理缓存释放空间;
如果设置为100,表示在内存不足时更倾向于保留缓存,使用swap空间。
很多程序调优时建议把这个值设置为0,因为使用swap 空间会让程序变得很卡顿。
vm.overcommit_kbytes 表示可过量分配的内存具体是多少,通常不做设置,为0 表示不关心这个参数;
vm.overcommit_memory 可以设置三个值,分别是0/1/2,为0 表示总是尝试过量分配,也可以说是尽可能地分配;为1 表示无脑过量分配,要多少给多少,哪怕是系统崩溃;为2 表示可过量分配的内存是RAM+swap 空间的总和 乘以一个百分比,百分比是由vm.overcommit_ratio 这个参数控制的,现在这个值是0,为0 或1 时不关心vm.overcommit_ratio 这个参数的值,只有为2 时才关心。
过量分配可能会导致内存溢出。
一旦内存溢出,就会触发OOM Killer 结束系统中的一些进程,用于缓解内存紧张,有可能会结束一些关键进程,可以设置保护哪些进程不被OOM Killer 结束。
进程ID 是 26730
每一个进程这里会有oom 的值,adj 是调整的意思,score 类似于票数的意思,每个进程都有这两个值,默认情况是0,也可能不是0
oom_adj:取值范围是 -17 ~ 15,数字越小,优先级越高,被kill 的概率越小,反之被kill 的概率越大。
可以看到oom_score 的值是随着oom_adj 的值自动变化的。
但是如果设置为 -17,oom_score 的值还是0,虽然显示为0,但是优先级还是比0 要高的,也就是相比于oom-adj 值为0 的进程优先被保护。
调到最大。
这样设置是临时生效的,如果想要永久生效可以用写systemd 服务的方法,具体不展开。
安装一下内核帮助文档,我们使用内核自带的功能模拟内存溢出场景:
vsftpd 进程被OOM Killer 结束了。
在实验过程中,发现有一个服务进程sssd_nss 被kill 之后立刻自动重启。
这个故障自愈的效果是怎么实现的呢?
它的服务配置文件里面有这一行,以后自己用systemd 管理别的服务也可以写这一行来实现故障自愈的功能。
我们在安装数据库的时候,经常会有一些这样的设置。
shm 是share memory(共享内存)的缩写。
kernel.shmmni 系统一共可以运行4096 个进程,每个进程最大可以使用kernel.shmmax 的共享内存。
kernel.shmall 系统一共可以分配多少空间给共享内存,单位是页。
共享内存是用来做进程间通讯,多个进程间需要共享内存。
算出来这个值,案例中设置的是64G
上面是内存溢出,下面讲的是内存泄露。
内存泄露:由于应用程序开发出现bug,导致应用程序在关闭后,不能完全释放内存,windows 系统经常出现这个问题,随着计算机运行时间越来越久,内存泄露的空间越来越多,系统越来越卡顿,甚至死机,解决方案就是重启。
从运维的角度看,我们没有办法解决内存泄露的问题,只能检测。
有一个专门的工具,可以检测内存泄露的问题,叫valgrind
比如说,检测一下ls 程序:
这个肯定是没有内存泄露的。
bigmem 这个程序有内存泄露的问题。
这样大概能定位到造成内存泄露的原因。
UMA 架构:一致性内存内存访问,CPU 通过FSB(前端总线)与内存交换数据,FSB 可能成为性能瓶颈。
NUMA:非一致性内存访问,把某几个CPU 和某几条内存放在一个NUMA Node 里面,交换数据的时候尽可能在一个NUMA Node 里面进行,当这个NUMA Node 里面资源不够,它可以通过QPI(快速互联通道)去其他NUMA Node 获取资源,这种架构可以有效减少CPU 访问内存的总线开销,NUMA Node 之间是全互联的(两两之间互联)。
如今大部分服务器都支持NUMA 架构,但不一定是有这么多NUMA Node
这是华为的虚拟化平台FusionCompute,这里有一个开关,叫做虚拟机NUMA 结构自动化调整,这个功能打开意味着虚拟机开机的时候尽量给它分配在同一个NUMA Node 里面的CPU 和内存资源,但这样做有一个缺点:无法做内存复用。
这个实验有条件最好在物理服务器上做,红帽教室环境用的是kvm/qemu虚拟机,本来是没有办法做的,官方内置了脚本可以用来模拟:
其实就是在这里加了参数。
上面有显示每一个node 分配的内存,size 是内存大小,free 是剩余内存大小,然后是node 与node 之间的距离,每一个node 与node 之间的距离都是10,可以理解为总线开销,这是模拟出来的NUMA Node,如果是物理机,一般node 两两之间距离不是相等的,比如node0 到node 0 距离是10,node0 到node 1 可能是20
使用bigmem 申请128M 内存,暂不退出:
查看bigmem 运行在哪些numa node 上:
看Private 那一行,只有Node 0 和Node 1 这两列数字为非0,运行在Node 0 和Node 1 上了。
指定运行在Node 1 上:
申请了1024M 内存,系统还是尽可能地分配了,但实际上Node 1 只有762 M 的内存资源。
指定程序可以运行在所有的NUMA Node 里面:
在系统中,用df 命令,你会经常看到tmpfs 这个设备:
这都是内存中的数据,1.9G 刚好是系统内存空间的一半,默认系统会分配一半的内存空间给tmpfs 设备。
上面有/dev/shm 这个挂载点,相当于把内存空间挂载到这里了。
这里面什么都没有。
swap:将硬盘当做内存用,内存紧张时会使用swap;
shm:将内存当磁盘用,提升性能。
比如典型的服务 squid,叫做代理服务器,用在web 网站,提升网站的响应速度。
对比写入速度差异明显。
因为是内存中的数据,重启以后就丢失了。
这里有一行配置,默认是注释的,这是配置一个缓存目录,最多占用100M的空间,生成16 个一级目录,每个一级目录里面生成256 个二级目录。
下面这条命令是把两个挂载点相关联,相当于往/var/spool/squid 里面读写数据是往/dev/shm 里面读写:
如果我想把shm 目录的空间进行调整,下面这样操作:
PTE:记录虚拟内存 --- 物理内存映射关系
CPU MMU:内存管理单元负责映射
TLB:映射表提供缓存 hugepage 大页
系统为应用程序分配虚拟内存,实际在物理内存分配的时候要预留一部分空间,用于缓存和映射表之类的,物理内存到虚拟内存的映射是由cpu 的MMU(内存管理单元)实现的,当应用程序打开的时候,一个映射表就产生了,应用程序关闭,映射关系就解除了,下次打开这个应用程序又要重新生成映射,这会增加cpu 的计算开销,所以为了提升性能,我们可以把物理内存到虚拟内存的映射表缓存下来,这个技术叫做TLB
注意:内存有页、大页之分
page size:页大小 4KiB
hugepage size:大页大小 2MiB
系统默认没有分配大页。
大页必须是一片连续的地址空间,且不能被应用程序所申请。
这个参数值是个数,默认0个,表示不分配大页。
分配大页内存之前的状态。
把它改为100,即分配100*2M = 200M 的大页内存。
分配之后。
bigmem 程序可以测试申请大页:
申请了50M,分配了25个,所以剩余75 个。
不是所有的应用程序都可以调用大页,需要代码支持。
需要程序包含mmap 的系统调用,否则不支持大页。
如果应用程序不支持大页怎么办?而且手动分配大页其实一开始的时候我们也不知道应该分配多少。从RHEL 6.2 的内核开始,Linux 支持透明大页。
中括号里面的always 表示是启用了透明大页,如果是never 就是没有启用。
透明大页默认是启用的,某些场景可能出现内存使用异常。
出现内存使用异常的时候,就要考虑是不是透明大页导致的,上图是临时解决方法,如果想要永久生效,可以通过写tuned 配置文件实现。
这个案例,要解决这个问题,需要在宿主机上开启”内存气泡“功能,也有叫“内存气球”的,自动回收内存空间,需要安装virio balloon 驱动,红帽的RHEV(企业版KVM 虚拟化)用的驱动其实就是开源社区ovirt 的驱动,可以访问ovirt 官网下载这个驱动,然后在KVM 虚拟机配置文件里面加入相关配置,重启该虚拟机后生效,具体不展开了。
swap 分区挂载的时候是可以指定优先级的
现在系统中有两个swap 分区,挂载的时候加一个pri 优先级参数,都指定为2
当优先级一样的时候,会怎么工作呢?
现在使用都为0
等待一段时间后,swap 使用的空间几乎是平均的。
优先级设置的意义,一个场景是跨硬盘的swap 分区,避免数据分散在不同的硬盘上影响性能。
swap 分区的优先级数字越大,优先级越高。
swapoff 命令卸载swap 分区的时候,如果里面的数据是有用的,不会丢失,会回写到内存。
持续更新中...