系统相关
首页 > 系统相关> > 在2G物理内存的机器上申请4G会怎么样? -- Linux内存管理

在2G物理内存的机器上申请4G会怎么样? -- Linux内存管理

作者:互联网

操作系统虚拟内存


应用程序通过malloc函数申请内存的时候,实际申请的是虚拟内存,并不会分配物理内存

当应用程序读写了这块虚拟内存,CPU就会去访问这个虚拟内存,这时会发现虚拟内存没有映射到物理内存,CPU就会产生缺页中断,进程会从用户态切换到内存态,并将缺页中断交给内核的page Fault handler(缺页中断函数)处理。

缺页中断处理函数会看是否有空闲的物理内存

32位操作系统和64位操作系统的虚拟地址空间大小是不同的,在Linux操作系统中,虚拟地址空间的内部又被分为内核空间和用户空间两部分:

image

在32位操作系统上,进程最多申请3GB大小的虚拟内存空间,所以进程申请8GB内存的话,在申请虚拟内存阶段就会失败(可能错误原因是OOM)

在64位操作系统,进程可以使用128T大小的虚拟内存空间,所以进程申请8GB内存是没问题的,因为进程申请内存是申请虚拟内存,只要不读写这个虚拟内存,操作系统就不会分配物理内存。


测试方案:

先查寻当前设备物理内存

free -m

在机器上连续申请4次1GB内存,只单纯分配了虚拟内存,并没有使用此虚拟内存

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define MEM_SIZE 1024 * 1024 * 1024

int main() {
    char* addr[4];
    int i = 0;
    for(i = 0; i < 4; ++i) {
        addr[i] = (char*) malloc(MEM_SIZE);
        if(!addr[i]) {
            printf("执行 malloc 失败, 错误:%s\n",strerror(errno));
          return -1;
        }
        printf("主线程调用malloc后,申请1gb大小得内存,此内存起始地址:0X%x\n", addr[i]);
    }
    
    //输入任意字符后,才结束
    getchar();
    return 0;
}

虽然物理内存只有2GB,但是程序正常分配了4G大小的虚拟内存:

image

通过一下命令查看进程(test)的虚拟内存大小:

ps aux 

其中,VSZ 就代表进程使用的虚拟内存大小,RSS 代表进程使用的物理内存大小。可以看到,VSZ 大小为 4198540,也就是 4GB 的虚拟内存。

image
image



Swap机制的作用

如果申请物理内存大小超过了空闲物理内存的大小,就要看操作系统有没有开启Swap机制:


什么是Swap机制?

当系统的物理内存不够用的时候,就需要将物理内存中的一部分空间释放出来,以供当前运行程序使用。那些被释放的空间可能来自一些很长时间没有什么操作的程序,这些被释放的空间会被临时保存到磁盘,等到那些程序要运行时,再从磁盘中恢复数据到内存中。

当内存使用存在压力的时候,会开始触发内存回收行为,会把这些不常访问的内存先写到磁盘中,然后释放这些内存,给其他更需要的进程使用。再次访问这些内存时,重新从磁盘中读入内存就可以了。

这种,将内存数据换出磁盘,又将磁盘中恢复数据到内存的过程,就是Swap机制负责的。

Swap就是把一块磁盘空间或者本地文件,当成内存来使用,包含换出和换入两个过程:

image

使用 Swap 机制优点是,应用程序实际可以使用的内存空间将远远超过系统的物理内存。由于硬盘空间的价格远比内存要低,因此这种方式无疑是经济实惠的。当然,频繁地读写硬盘,会显著降低操作系统的运行速率,这也是 Swap 的弊端。


Linux中的Swap机制会在内存不足和内存闲置的场景下触发:


Linux 提供了两种不同的方法启用 Swap,分别是 Swap 分区(Swap Partition)和 Swap 文件(Swapfile):

Swap换入换出的是什么类型的内存?

内核缓存的文件数据,因为都有对应的磁盘文件,所以在回收文件数据的时候, 直接写回到对应的文件就可以了。

但是像进程的堆、栈数据等,它们是没有实际载体,这部分内存被称为匿名页。而且这部分内存很可能还要再次被访问,所以不能直接释放内存,于是就需要有一个能保存匿名页的磁盘载体,这个载体就是 Swap 分区。

匿名页回收的方式是通过 Linux 的 Swap 机制,Swap 会把不常访问的内存先写到磁盘中,然后释放这些内存,给其他更需要的进程使用。再次访问这些内存时,重新从磁盘读入内存就可以了。

实验测试:

我的Linux系统镜像是32位操作系统,物理内存设置2GB,带有Swap分区:

image

测试代码直接申请4GB虚拟内存后,通过memset函数访问

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define MEM_SIZE 1024 * 1024 * 1024

int main() {
    char* addr[4];
    int i = 0;
    for(i = 0; i < 4; ++i) {
        addr[i] = (char*) malloc(MEM_SIZE);
        if(!addr[i]) {
            printf("执行 malloc 失败, 错误:%s\n",strerror(errno));
            return -1;
        }
        printf("主线程调用malloc后,申请1gb大小得内存,此内存起始地址:0X%x\n", addr[i]);
    }

    for(i = 0; i < 4; ++i) {
        printf("开始访问第 %d 块虚拟内存(每一块虚拟内存为 1 GB)\n", i + 1);
        memset(addr[i], 0, MEM_SIZE);
    }
    
    //输入任意字符后,才结束
    getchar();
   
    return 0;
}

运行结果:

image

结论:

在有Swap机制的情况下,虽然物理内存只有2GB,也是可以申请4GB虚拟内存并使用的

修改测试代码:

申请8G内存(因为物理内存加Swap区接近8G),测试结果如下:

image


8G内存可以正常申请,但是当访问前7G虚拟内存区域时都是正常的,当访问第8G时,被系统kill,意味着发生了OOM,查询日志文件,发现确实kernel发生了oom-kill。

image

总结

因为手头暂时没有现成的64位镜像,所64位结论采用参考文章的结论

标签:--,申请,2G,内存,进程,物理,虚拟内存,Swap
来源: https://www.cnblogs.com/Wangzx000/p/16486039.html