你真的理解内存分配吗?
内存是计算机中必不可少的资源,因为 CPU 只能直接读取内存中的数据,所以当 CPU 需要读取外部设备(如硬盘)的数据时,必须先把数据加载到内存中。
我们来看看可爱的内存长什么样子的吧,如图1所示:
一、内存申请
通常使用高级语言(如Go、Java 或 Python 等)都不需要自己管理内存(因为有垃圾回收机制),但 C/C++ 程序员就经常要与内存打交道。
当我们使用 C/C++ 编写程序时,如果需要使用内存,就必须先调用 malloc
函数来申请一块内存。但是,malloc
真的是申请了内存吗?
我们通过下面例子来观察 malloc
到底是不是真的申请了内存:
1#include <stdlib.h>23int main(int argc, char const *argv[])4{5 void *ptr;67 ptr = malloc(1024 * 1024 * 1024); // 申请 1GB 内存89 sleep(3600); // 睡眠3600秒, 方便调试
10
11 return 0;
12}
上面的程序主要通过调用 malloc
函数来申请了 1GB 的内存,然后睡眠 3600 秒,方便我们查看其内存使用情况。
现在,我们编译上面的程序并且运行,如下:
1$ gcc malloc.c -o malloc
2$ ./malloc
并且我们打开一个新的终端,然后查看其内存使用情况,如图 2 所示:
图2 中的 VmRSS
表示进程使用的物理内存大小,但我们明明申请了 1GB 的内存,为什么只显示使用 404KB 的内存呢?这里就涉及到 虚拟内存
和 物理内存
的概念了。
二、物理内存与虚拟内存
下面先来介绍一下 物理内存
与 虚拟内存
的概念:
物理内存
:也就是安装在计算机中的内存条,比如安装了 2GB 大小的内存条,那么物理内存地址的范围就是 0 ~ 2GB。虚拟内存
:虚拟的内存地址。由于 CPU 只能使用物理内存地址,所以需要将虚拟内存地址转换为物理内存地址才能被 CPU 使用,这个转换过程由MMU(Memory Management Unit,内存管理单元)
来完成。虚拟内存
大小不受物理内存
大小的限制,在 32 位的操作系统中,每个进程的虚拟内存空间大小为 0 ~ 4GB。
程序中使用的内存地址都是虚拟内存地址,也就是说,我们通过 malloc
函数申请的内存都是虚拟内存。实际上,内核会为每个进程管理其虚拟内存空间,并且会把虚拟内存空间划分为多个区域,如 图3 所示:
我们来分析一下这些区域的作用:
代码段
:用于存放程序的可执行代码。数据段
:用于存放程序的全局变量和静态变量。堆空间
:用于存放由malloc
申请的内存。栈空间
:用于存放函数的参数和局部变量。内核空间
:存放 Linux 内核代码和数据。
三、brk指针
由此可知,通过 malloc
函数申请的内存地址是由 堆空间
分配的(其实还有可能从 mmap
区分配,这种情况暂时忽略)。在内核中,使用一个名为 brk
的指针来表示进程的 堆空间
的顶部,如 图4 所示:
所以,通过移动 brk
指针就可以达到申请(向上移动)和释放(向下移动)堆空间的内存。例如申请 1024 字节时,只需要把 brk
向上移动 1024 字节即可,如 图5 所示:
事实上,malloc
函数就是通过移动 brk
指针来实现申请和释放内存的,Linux 提供了一个名为 brk()
的系统调用来移动 brk
指针。
四、内存映射
现在我们知道,malloc
函数只是移动 brk
指针,但并没有申请物理内存。前面我们介绍虚拟内存和物理内存的时候介绍过,虚拟内存地址必须映射到物理内存地址才能被使用。如 图6 所示:
如果对没有进行映射的虚拟内存地址进行读写操作,那么将会发生 缺页异常
。Linux 内核会对 缺页异常
进行修复,修复过程如下:
获取触发
缺页异常
的虚拟内存地址(读写哪个虚拟内存地址导致的)。查看此虚拟内存地址是否被申请(是否在
brk
指针内),如果不在brk
指针内,将会导致 Segmention Fault 错误(也就是常见的coredump),进程将会异常退出。如果虚拟内存地址在
brk
指针内,那么将此虚拟内存地址映射到物理内存地址上,完成缺页异常
修复过程,并且返回到触发异常的地方进行运行。
从上面的过程可以看出,不对申请的虚拟内存地址进行读写操作是不会触发申请新的物理内存。所以,这就解释了为什么申请 1GB 的内存,但实际上只使用了 404 KB 的物理内存。
五、总结
本文主要解释了内存申请的原理,并且了解到 malloc
申请的只是虚拟内存,而且物理内存的申请延迟到对虚拟内存进行读写的时候,这样做可以减轻进程对物理内存使用的压力。
推荐阅读:
专辑|Linux文章汇总
专辑|程序人生
专辑|C语言
我的知识小密圈
关注公众号,后台回复「1024」获取学习资料网盘链接。
欢迎点赞,关注,转发,在看,您的每一次鼓励,我都将铭记于心~
你真的理解内存分配吗?相关推荐
- 你确定你理解内存分配吗?
内存是计算机中必不可少的资源,因为 CPU 只能直接读取内存中的数据,所以当 CPU 需要读取外部设备(如硬盘)的数据时,必须先把数据加载到内存中. 内存是计算机中必不可少的资源,因为 CPU 只能直 ...
- 一文读懂 Linux 内存分配全过程
在<你真的理解内存分配>一文中,我们介绍了 malloc 申请内存的原理,但其在内核怎么实现的呢?所以,本文主要分析在 Linux 内核中对堆内存分配的实现过程. 本文使用 Linux 2 ...
- java 线程 内存分配内存_漫谈JAVA语言的内存分配
在说JAVA语言的内存分配之前,我们先聊聊OS的Memory Management,这是学习操作系统课程的一个重要内容.从这里开始理解内存分配有利于理解的更深入. 我们知道物理内存是由page 和se ...
- c语言中alloc作用,C语言内存分配 :malloc()函数与alloc()函数
C语言跟内存分配方式 (1) 从静态存储区域分配.内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在.例如全局变量,static变量. (2) 在栈上创建.在执行函数时,函数内局部变 ...
- Linux内存分配--实现FF、BF、WF分配算法
v 通过深入理解内存分配管理的三种算法,定义相应的数据结构,编写具体代码. 充分模拟三种算法的实现过程,并通过对比,分析三种算法的优劣. v ( 1 )掌握内存分配 FF , BF , WF 策略及实 ...
- 《深入理解Java虚拟机》-----第3章 垃圾收集器与内存分配策略
Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的"高墙",墙外面的人想进去,墙里面的人却想出来. 3.1 概述 说起垃圾收集(Garbage Collection,G ...
- 《深入理解java虚拟机》笔记2——GC算法与内存分配策略
说起垃圾收集(Garbage Collection, GC),想必大家都不陌生,它是JVM实现里非常重要的一环,JVM成熟的内存动态分配与回收技术使Java(当然还有其他运行在JVM上的语言,如Sca ...
- 深入理解JVM(2)——GC算法与内存分配策略
说起垃圾收集(Garbage Collection, GC),想必大家都不陌生,它是JVM实现里非常重要的一环,JVM成熟的内存动态分配与回收技术使Java(当然还有其他运行在JVM上的语言,如Sca ...
- [转载] Java内存管理-你真的理解Java中的数据类型吗(十)
参考链接: Java中的字符串类String 1 做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开! 推荐阅读 第一季 0.Java的线程安全.单例模式.JVM内存结构等知识 ...
最新文章
- MySQL中优化sql语句查询常用的30种方法
- 输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示
- 你不会真的以为自己懂得计算机网络吧?
- unix下批量进程的创建和强杀命令
- 比亚迪定薪后多久给offer_比亚迪车主给爱车做四门隔音,没想到两年后肠子都悔青...
- java 数据包含_java – 包含大量数据库表的通用实现
- Windows10如何安装windows terminal
- ZXPInstaller for mac(zxp文件安装器)免费版
- PPT幻灯片放映不显示备注,只让备注显示在自己屏幕上!
- 傻子都能看懂的 财务报表入门
- 六【Java 基础】数组的概述: 数组的定义与使用
- 效果 - 收藏集 - 掘金
- Unity基础包 刚体FPS HeadBob和CameraRefocus 脚本研究
- 轮播图的做法(更换背景图片)
- 什么是FPGA工程师的核心竞争力
- BIOS 选项中英文对照
- 颜色十六进制代码表 | HTML Color Codes Table
- java 打包工具jar,打包工具类为jar包,注意事项
- Android(安卓)APP渗透测试-总结
- python数据分析vip班级笔记