虚拟内存详解

系统监控报警,内存空间不足,但实际可用内存还是剩很多的,这是为什么?

究其原因,监控系统计算的可用内存算法有偏差,他只关注了计算机的“实际”内存,忽略了计算机的虚拟内存。

问题又来了,什么是虚拟内存?他有什么作用?他和“实际”内存有什么关系?且听娓娓道来。

计算机内存分为物理内存与虚拟内存。物理内存是计算机的实际内存大小,由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位系统内并不能使用。

2. JVM进程占用虚拟内存过多

使用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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Linux实际内存占用率算法,以及使用Python实现内存监控

    这两天我们的一个核心系统,一套集群,逐台开始报警,内容是内存占用超阈值。按说这应该是一个非常紧急且需要立即处理的报警,但实际是不是这样,待我们拨云见日。

    bisal
  • 【每日一摩斯】-Fundamentals of the Large Pool

           大池是SGA中一块类似于shared pool的区域,但是它的使用又有严格的限制,仅有几种类型和大小的内存能够在这个池中分配。

    bisal
  • 【Oracle】-【LRU和DBWR】-LRU算法与DBWR中的应用

    Oracle体系结构中经常看到LRU算法,Least Recently Used,也有叫“最近最少使用页面置换算法”,简单讲...

    bisal
  • 剑指内存泄漏

    指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,失去了对该段内存的控制,因而造成了内存的...

    audy
  • SQL Server内存

    背景 最近一个客户找到我说是所有的SQL Server 服务器的内存都被用光了,然后截图给我看了一台服务器的任务管理器。如图 ? 这里要说明一下任务管理器不会完...

    用户1217611
  • 故障分析 | MySQL OOM 故障应如何下手

    前阵子处理这样一个案例,某客户的实例 mysqld 进程内存经常持续增加导致最终被 OOM killer。作为 DBA 肯定想知道有哪些原因可能会导致 OOM(...

    爱可生开源社区
  • 软件性能测试(连载9)

    Linux内核给每个进程都提供了一个独立的虚拟地址空间,并且这个地址空间是连续的。Linux的空间又分为内核空间和用户空间,在32位中,内核空间占1G,用户空间...

    小老鼠
  • Java架构师中的内存溢出和内存泄露是什么?实际操作案例!

    申请了内存用完了不释放,比如一共有 1024M 的内存,分配了 521M 的内存一直不回收,那么可以用的内存只有 521M 了,仿佛泄露掉了一部分;

    Java架构师进阶技术
  • 开发应该知道的Linux系统分析-内存篇

    用free监控内存free是监控linux内存使用状况最常用的指令,看下面的一个输出

    只喝牛奶的杀手
  • 移动APP测试之android性能测试

      当应用实现了新功能后,准备发布版本前,必须进行性能测试以确定没有性能问题,内存使用情况便是其中必须要测试的性能之一。由于内存组成的复杂性,并没有简单通用的方...

    小老鼠

扫码关注云+社区

领取腾讯云代金券