系统监控报警,内存空间不足,但实际可用内存还是剩很多的,这是为什么?
究其原因,监控系统计算的可用内存算法有偏差,他只关注了计算机的“实际”内存,忽略了计算机的虚拟内存。
问题又来了,什么是虚拟内存?他有什么作用?他和“实际”内存有什么关系?且听娓娓道来。
计算机内存分为物理内存与虚拟内存。物理内存是计算机的实际内存大小,由RAM芯片组成。虚拟内存则是虚拟出来的、使用磁盘代替内存。虚拟内存的出现,让机器内存不够的情况得到部分解决。当程序运行起来由操作系统做具体虚拟内存到物理内存的替换和加载(相应的页与段的虚拟内存管理)。
毋庸置疑,虚拟内存绝对是操作系统中最重要的概念之一。主要是由于内存的重要”战略地位”。CPU太快,但容量小且功能单一,其他I/O硬件支持各种花式功能,可是相对于CPU,他们又太慢。于是它们之间就需要一种润滑剂来作为缓冲,这就是内存大显身手的地方。
而在现代操作系统中,多任务已是标配。多任务并行,大大提升了CPU利用率,但却引出了多个进程对内存操作的冲突问题,虚拟内存概念的提出就是为了解决这个问题。
这张图是虚拟内存最简单也是最直观的解释,
操作系统有一块物理内存(中间的部分),有两个进程(实际会更多)P1和P2,操作系统偷偷地分别告诉P1和P2,我的整个内存都是你的,随便用,管够。可事实上呢,操作系统只是给它们画了个大饼,这些内存说是都给了P1和P2,实际上只给了他们一个序号而已。只有当P1和P2真正开始使用这些内存时,系统才开始使用辗转挪移,拼凑出各个块给进程用,P2以为自己在用A内存,实际上已经被系统悄悄重定向到真正的B去了,甚至,当P1和P2共用了C内存,他们都是不知道。
操作系统的这种欺骗进程的手段,就是虚拟内存。对P1和P2等进程来说,它们都以为自己占用了整个内存,而自己使用的物理内存的哪段地址,它们并不知道也无需关心。
分页和页表。虚拟内存是操作系统里的概念,对操作系统来说,虚拟内存就是一张张的对照表,P1获取A内存里的数据时应该去物理内存的A地址找,而找B内存里的数据应该去物理内存的C地址。
我们知道系统里的基本单位都是Byte字节,如果将每一个虚拟内存的Byte都对应到物理内存的地址,每个条目最少需要8字节(32位虚拟地址->32位物理地址),在4G内存的情况下,就需要32GB的空间来存放对照表,那么这张表就大得真正的物理地址也放不下了,于是操作系统引入了页(Page)的概念。
在系统启动时,操作系统将整个物理内存以4K为单位,划分为各个页。之后进行内存分配时,都以页为单位,那么虚拟内存页对应物理内存页的映射表就大大减小了,4G内存,只需要8M的映射表即可,一些进程没有使用到的虚拟内存,也并不需要保存映射关系,而且Linux还为大内存设计了多级页表,可以进一页减少了内存消耗。操作系统虚拟内存到物理内存的映射表,就被称为页表。
内存寻址和分配。我们知道通过虚拟内存机制,每个进程都以为自己占用了全部内存,进程访问内存时,操作系统都会把进程提供的虚拟内存地址转换为物理地址,再去对应的物理地址上获取数据。CPU中有一种硬件,内存管理单元MMU(Memory Management Unit)专门用来将翻译虚拟内存地址。CPU还为页表寻址设置了缓存策略,由于程序的局部性,其缓存命中率能达到 98%。
以上情况是页表内存在虚拟地址到物理地址的映射,而如果进程访问的物理地址还没有被分配,系统则会产生一个缺页中断,在中断处理时,系统切到内核态为进程虚拟地址分配物理地址。
机器物理内存,读写速度低于CPU一个量级,但是高于磁盘不止一个量级。所以,程序和数据如果在内存的话,会有非常快的读写速度。但是,内存的造价是要高于磁盘的,且内存的断电丢失数据也是不能把所有数据和程序都保存在内存中的原因。
既然不能全部使用内存,那数据还有程序不可能一直霸占在内存中。当内存没有可用的,就必须要把内存中不经常运行的程序踢出去。但是踢到哪里去,这时候swap就出现了。
Swap全称为swap place,即交换分区。当内存不够的时候,被踢出的进程被暂时存储到交换区。当需要这条被踢出的进程时,就从交换区重新加载到内存,否则他不会主动交换到真实内存中。这里的swap即是虚拟内存。
swap机制的初衷是为了缓解物理内存用尽而选择直接粗暴OOM进程的尴尬。swap意思是交换,顾名思义,当某进程向OS请求内存发现不足时,OS会把内存中暂时不用的数据交换出去,放在swap分区中,这个过程称为swap out。当某进程又需要这些数据且OS发现还有空闲物理内存时,又会把swap分区中的数据交换回物理内存中,这个过程称为swap in。
当然,swap大小是有上限的,一旦swap使用完,操作系统会触发OOM-Killer机制,把消耗内存最多的进程kill掉以释放内存。
当用户提交程序,然后产生进程在机器上运行。机器会判断当前物理内存是否还有空闲允许进程调入内存运行,如果有则直接调入内存进行。如果没有,则会根据优先级选择一个进程挂起,把该进程交换到swap中等待,然后把新的进程调入到内存中运行。根据这种换入和换出,实现了内存的循环利用,让用户感觉不到内存的限制。从这也可以看出swap扮演了一个非常重要的角色,就是暂存被换出的进程。
内存与swap之间是按照内存页为单位来交换数据的,一般Linux中页的大小设置为4Kb。而内存与磁盘则是按照块来交换数据的。
当物理内存使用完或者达到一定比例之后,我们可以使用swap做临时的内存使用。当物理内存和swap都被使用完那么就会出错,如:out of memory。
对于使用多大比例内存之后开始使用swap,在系统配置文件中可以通过调整参数进行修改,
1 [root@localhost ~]# cat /proc/sys/vm/swappiness
2 60
该参数范围为0-100。0就是最大限度使用内存,尽量不使用swap;100是积极使用swap。
物理内存无法更改,所以swap大小的设置将会影响应用能否正常运行。swap大小的确定,根据Centos官网介绍可以得出如下公式:
1 M = Amount of RAM in GB, and S = Amount of swap in GB, then If M < 2, S = M *2 Else S = M + 2
注意:最小不应小于32M。
swap分区的数量对性能也有很大的影响。因为swap毕竟还是以磁盘来伪装成内存,交换的操作是磁盘IO的操作而不是内存的ioad与store操作。如果有多个swap交换区,每个swap会有一定的优先级,该优先级也可以调整。swap空间的分配会以轮流的方式操作于所有的swap,这样会大大均衡IO的负载,加快swap交换的速度。
几个和内存相关的问题。
1. 32位和64位
最常见的就是32位和64位的问题了。
CPU通过物理总线访问内存,那么访问地址的范围就受限于机器总线的数量,在32位机器上,有32条总线,每条总线有高低两种电位分别代表bit的1和0,那么可访问的最大地址就是2^32bit = 4GB,所以说32位机器上插入大于4G的内存是无效的,CPU访问不到多于4G的内存。
但64位机器并没有64位总线,而且其最大内存还要受限于操作系统,Linux 目前支持最大256G内存。
根据虚拟内存的概念,在32位系统上运行64位软件也并无不可,但由于系统对虚拟内存地址的结构设计,64位的虚拟地址在32位系统内并不能使用。
使用top查看系统性能时,我们会发现在VIRT这一列,Java进程会占用大量的虚拟内存,
导致这种问题的原因是Java使用Glibc的Arena内存池分配了大量的虚拟内存并没有使用。此外,Java读取的文件也会被映射为虚拟内存,在虚拟机默认配置下Java每个线程栈会占用1M的虚拟内存。具体可以查看为什么linux下多线程程序如此消耗虚拟内存。
而真实占用的物理内存要看RES(resident)列,这一列的值才是真正被映射到物理内存的大小。
在Linux系统中,启动一个程序,他占用的内存假设是1G,但是运行一段时间后,使用top查看进行信息,你会发现它的内存只剩几十M了,这是因为内存不足,他的内存被swap走了。
若开启了swap,则系统会有一个交换空间在硬盘里,你的内存数据正是被交换到硬盘里,因此程序会运行的比较慢。你可以用free -m命令查看交换空间使用了多少了。
参考文献:
https://blog.csdn.net/qq_36838191/article/details/82768303
https://www.cnblogs.com/zhenbianshu/p/10300769.html
https://www.cnblogs.com/pipci/p/11399250.html
近期热文:
《公众号600篇文章分类和索引》
《Oracle ACE,一段不可思议的旅程》
《插入"&"特殊字符的几种思考》
《Oracle 19c之RPM安装》
《应用执行慢的问题排查路径》
《ACOUG年会感想》
《千万级表数据更新的需求》
《探寻大表删除字段慢的原因》
《一次Oracle bug的故障排查过程思考》
《新增字段的一点一滴技巧》
《对recursive calls的深刻理解》
《《Oracle Concept》第三章 - 12》
《一次惊心动魄的问题排查》
《英超梦幻之行》
《藤子不二雄博物馆之行》
《传控Tiki-Taka战术解惑》