堆内存相关
定义和特点
- 堆内存是程序运行时动态分配的内存区域,由开发者手动申请和释放(如
C
中的malloc
/free
、C++
的new
/delete
) - 具有以下特点
- 生命周期长:内存存活周期由开发者控制,不随函数调用结束而释放
- 空间大且灵活:理论上可分配至系统可用内存上限,适合存储大型对象或动态数据结构(如链表、树)
- 分配速度慢:需通过内存管理器查找可用空间,可能触发内存整理或系统调用
- 管理复杂度高:易引发内存泄漏、碎片化、越界访问等问题
堆内存的分配原理
- 概述
- 堆内存分配依赖于操作系统的内存管理器,其核心流程为:
- 请求内存:程序通过
malloc
/new
申请指定大小的内存块 - 查找空闲块:内存管理器遍历空闲链表,寻找满足需求的连续空间(常用算法:首次适应、最佳适应)
- 分配与标记:若找到合适块,分割并标记为已使用;若不足则触发扩容(如通过
brk
或mmap
系统调用) - 释放与合并:调用
free
/delete
后,内存块被标记为空闲,并尝试合并相邻空闲块以减少碎片
Windows
内存管理器
概念
Windows
内存管理器是操作系统内核的核心组件(内核模式内存管理器),负责管理物理内存、虚拟内存以及内存映射文件等资源
Windows
内存管理器具体分配内存的过程
- 虚拟地址申请
- 进程通过
API
(如VirtualAlloc
)申请虚拟地址空间,系统根据请求大小保留或提交内存 - 内存分配粒度通常为
64KB
(区域粒度)和4KB
(页面粒度)
- 进程通过
- 物理内存映射
- 页目录与页表
虚拟地址通过CPU
的CR3
寄存器找到页目录表(Page Directory
),再通过页表(Page Table
)逐级转换为物理地址
例如,虚拟地址0x12345678
的高10
位定位页目录项,中间10
位定位页表项,低12
位为页内偏移 - 页面状态管理
页面可能处于空闲、保留、提交或共享状态
系统通过工作集(Working Set
)跟踪活跃页面
- 页目录与页表
- 内存池优化
- 系统缓存:缓存频繁访问的文件数据,减少磁盘
I/O
- 非分页池与分页池:内核组件使用非分页池(不可交换到页文件),驱动程序和用户程序使用分页池(可交换)
- 系统缓存:缓存频繁访问的文件数据,减少磁盘
Windows
内存管理器内存回收机制
- 内存回收通过动态调整和置换策略实现资源高效利用:
- 页面置换算法:
LRU
(最近最少使用):优先淘汰长时间未访问的页面- 工作集修剪:当物理内存不足时,系统将非活跃页面移至页文件,释放物理内存
- 内存压缩技术:
Windows 10
后引入内存压缩(压缩不活跃页面),减少对页文件的依赖,提升响应速度
- 显式释放与合并:
- 进程调用
VirtualFree
释放虚拟地址空间,系统标记页面为可回收 - 堆管理器(如
HeapFree
)合并相邻空闲块,减少外部碎片
- 进程调用
- 异常处理与保护:
- 访问非法地址或已释放内存时,触发
STATUS_ACCESS_VIOLATION
异常,终止进程防止系统崩溃
- 访问非法地址或已释放内存时,触发
物理内存
- 物理内存通过分页机制划分为固定大小的块(通常
4KB
),页文件(如pagefile.sys
)作为物理内存的扩展,用于存储不活跃的页面
物理内存的分页机制
分页机制的核心概念
- 分页机制是操作系统管理物理内存的核心技术,通过将物理内存划分为固定大小的块(页框,
Page Frame
),并将进程的虚拟地址空间划分为相同大小的页(Page
),实现虚拟内存与物理内存的动态映射 - 固定页大小:通常为
4KB
(如Windows
系统),也有大页(如8KB
或4MB
)用于特殊场景 - 离散分配:进程的页可以分散存储在物理内存的不同页框中,无需连续空间,解决了外部碎片问题
- 虚拟地址映射:通过页表(
Page Table
)实现逻辑地址到物理地址的转换,每个进程拥有独立的页表
分页机制的关键结构
- 页表与页目录表:
- 页表:存储虚拟页号到物理页框号的映射关系,每个页表项(
PTE
)占4
字节(32
位系统),包含物理页框号及权限标志(如读/写/执行) - 页目录表:在二级分页机制中,页目录表存储页表的物理地址,通过虚拟地址的高
10
位索引
CR3
寄存器:存储当前进程页目录表的物理地址,是地址转换的起点
- 页表:存储虚拟页号到物理页框号的映射关系,每个页表项(
- 多级页表
- 当物理内存超过
4GB
时,采用多级页表(如2-9-9-12
分页机制),扩展页表项为64
位以支持更大物理地址空间 - 例如,
Windows
系统的10-10-12
分页机制支持4GB
物理内存,而PAE
(物理地址扩展)模式通过三级页表支持更大内存
- 当物理内存超过
地址转换流程
- 虚拟地址到物理地址的转换通过硬件(
MMU
)完成,步骤如下:- 拆分虚拟地址:例如
32
位地址拆分为高10
位(页目录索引)、中间10
位(页表索引)、低12
位(页内偏移) - 查页目录表:通过
CR3
寄存器找到页目录表基址,用高10
位索引获取页表物理地址 - 查页表:用中间
10
位索引页表,获取物理页框号 - 拼接物理地址:物理页框号左移
12
位(4KB
对齐)后加上页内偏移,得到最终物理地址
- 拆分虚拟地址:例如
- 示例:
- 虚拟地址
0x1D16H
(二进制0001 1101 0001 0110
)在4KB
分页下,页号为1
,页内偏移为0xD16
,若页表项指向物理页框3
,则物理地址为0x3D16H
- 虚拟地址
物理内存相关总结(记忆)
- 物理内存是通过分页机制被划分为许多固定大小的块(页框或者物理页,
Page Frame
) - 每个页框都有唯一的编号,用来标识它在物理内存中的位置
- 而这些页框的编号和权限信息,又会被页表保存
- 在多级分页机制中,一级页表(或者叫页目录表)保存了二级页表的物理地址,而二级页表则保存了多个虚拟页到物理页(也就是页框)的映射关系,
- 在
32
位系统里面,CPU
的CR3
寄存器保存了页目录表的物理地址,而虚拟地址的高10
位是页目标表的索引,中间10
位则是页表的索引,低12
位则是页内的偏移 - 虚拟地址和物理地址就是这样映射的
虚拟内存
- 通过分页技术(
Page File
)将物理内存与硬盘空间结合,为每个进程提供独立的4GB
虚拟地址空间(32
位系统) - 虚拟内存分为三个主要区域:
- 用户模式分区(
0x00000000-0x7FFFFFFF
):进程独享,存储代码、堆、栈等数据 - 内核模式分区(
0x80000000-0xFFFFFFFF
):共享系统内核、驱动程序和内存池 NULL
指针保护区(0x00000000-0x0000FFFF
):防止非法访问空指针
- 用户模式分区(
虚拟内存的分页技术
分页机制的核心概念
- 分页技术是虚拟内存的核心实现机制,通过将虚拟内存与物理内存划分为固定大小的页(如
4KB
),并建立页表实现地址映射,其核心逻辑如下:
分页机制的基本结构
- 页与页框
- 虚拟内存空间被划分为虚拟页(
Page
),物理内存划分为页框(Frame
),两者大小相同(如4KB
)
- 虚拟内存空间被划分为虚拟页(
- 页表
- 每个进程拥有独立的页表,页表项(
PTE
)保存虚拟页到物理页框的映射关系及权限标志(存在位、读写位等)
- 每个进程拥有独立的页表,页表项(
- 多级页表
32
位系统采用二级页表(页目录表+页表),64
位系统使用四级页表(PGD→PUD→PMD→PTE
),通过分级减少页表内存占用
分页技术的关键特性
- 按需加载:
- 虚拟页仅在需要时加载到物理内存,未使用的页保留在硬盘(
Page File
)中,减少物理内存占用
- 虚拟页仅在需要时加载到物理内存,未使用的页保留在硬盘(
- 内存保护:
- 通过页表项的权限位(如用户/内核模式、读写限制)隔离进程内存空间,防止非法访问
- 缺页中断(
Page Fault
):- 当访问的虚拟页未在物理内存中时,触发缺页中断,操作系统从硬盘加载该页到内存并更新页表
硬件支持
MMU
(内存管理单元):- 负责虚拟地址到物理地址的转换,通过页表和
TLB
(快表)加速映射
- 负责虚拟地址到物理地址的转换,通过页表和
CR3
寄存器:- 存储当前进程页目录表的物理地址,是地址转换的起点
分页技术如何结合硬盘空间提供4GB
独立地址空间
- 在
32
位系统中,虚拟地址空间为4GB
,但物理内存通常远小于此。分页技术通过以下方式实现扩展: - 页表映射与硬盘交换
- 虚拟地址划分:
32
位虚拟地址分为三部分:
高10
位:索引页目录表(一级页表),定位二级页表的物理基址
中间10
位:索引二级页表,获取物理页框号
低12
位:页内偏移量(4KB
对齐) - 硬盘空间作用
当物理内存不足时,操作系统将不活跃的页换出到硬盘的Page File
中,腾出空间加载新页
若进程访问的页在硬盘中,触发缺页中断,由操作系统将页从硬盘加载到物理内存,并更新页表项的存在位
- 虚拟地址划分:
- 进程地址空间的独立性
- 独立页表:每个进程拥有独立的页目录表和页表,通过
CR3
寄存器切换页目录基址,确保进程间内存隔离 - 虚拟地址到物理地址的动态映射:
进程的4GB
虚拟地址空间通过页表映射到物理内存或硬盘中的Page File
,实际物理内存分配是动态且离散的 - 例如,进程
A
的虚拟地址0x1000
可能映射到物理页框0x2000
,而进程B
的同一虚拟地址0x1000
可能映射到物理页框0x5000
或硬盘中的某个位置
- 独立页表:每个进程拥有独立的页目录表和页表,通过
- 性能优化与局限性
TLB
加速:高频访问的页表项缓存在TLB
中,减少查多级页表的开销- 硬盘延迟问题:频繁换页会导致性能下降(尤其是使用
HDD
时),SSD
可缓解此问题 - 内存压缩技术:部分系统(如
macOS
)压缩内存数据,减少换页需求
页表项
- 页表项(
PTE
)不仅包含物理页框编号和权限,还包括以下关键标志位- 存在位(
Present, P
):标记该页是否已加载到物理内存(若为0
,访问会触发缺页中断) - 读写权限(
R/W
):控制页面是否可写(1=
可读写,0=
只读/执行) - 用户/内核权限(
U/S
):标记页面是否允许用户态程序访问(1=
允许,0=
仅内核态访问) - 访问位(
Accessed, A
):记录该页是否被CPU
访问过(用于页面置换算法) - 脏位(
Dirty, D
):标记页内容是否被修改(若为1
,换出时需写回磁盘) - 全局位(
Global, G
):标记是否为全局页(TLB
缓存中不失效,常用于内核页) - 高速缓存控制位(
PWT/PCD
):控制页面的缓存策略(如直写/回写、是否禁用缓存)
- 存在位(
虚拟地址到物理地址的映射机制
VirtualAlloc
等函数通过以下步骤建立映射:- 分配虚拟地址范围(不立即分配物理内存)
- 按需填充页目录表和页表项(物理页框可能在首次访问时分配)
- 通过缺页中断触发物理页框的实际分配或从磁盘加载
虚拟内存相关总结(记忆)
- 首先,虚拟内存是通过页表映射和硬盘交换的技术为进程提供了逻辑上的
4GB
地址空间 - 然后,这个虚拟地址空间也会被划分为固定大小的页(也叫虚拟页),具体大小与物理内存的页框一致
- 进程启动时,操作系统仅为进程分配虚拟地址空间,但不会立即分配全部物理内存
- 当进程首次访问位于某个虚拟页的虚拟地址时,通过每个进程自己的页表,发现其对应的页表项可能处于未分配或未加载状态,就会触发缺页中断
- 如果是虚拟页未分配物理页框,需从磁盘(如
Page File
或文件)加载数据到内存,并更新页表项 - 如果虚拟页已分配物理页框,但尚未加载到内存(如共享库已被其他进程加载)。此时仅需将已存在的物理页框与虚拟页关联
内存保护机制
- 通过权限控制(如可读、可写、可执行)和地址空间隔离,防止进程间内存冲突
疑问与理解
-
关于页目录表和页表的理解
CPU
的CR3
寄存器保存了进程的页目录表的物理地址- 而在进程的页目录表里面,可能保存了二级页目录表的物理基址(假如存在二级页目录表)
- 而二级页目录表里面保存了很多页表项
- 页表项的内容有物理页框编号,页框的相关权限
-
操作系统为每一个进程维护了一个页表目录吗?
- 操作系统为每个进程维护独立的页目录表,目的是实现进程间内存隔离和按需动态映射:
- 进程切换与
CR3
寄存器:CPU
通过CR3
寄存器存储当前进程的页目录表物理地址
当进程切换时,操作系统更新CR3
寄存器,指向新进程的页目录表,从而实现不同进程的地址空间隔离 - 页目录表的独立性:每个进程的页目录表仅包含其实际使用的虚拟地址区域的映射关系,未使用的二级页表可能不存在,按需分配
- 内核空间的共享性:虽然用户空间的页目录表独立,但内核空间的页表项(如操作系统内核代码和数据)可能被所有进程共享,通过全局页表(
Global Page Table
)实现
-
物理内存不足时的换页机制
- 当物理内存不足且访问的页未加载(存在位
P=0
)时,触发缺页中断 - 作系统根据页面置换算法(如
LRU
、时钟算法)选择不活跃的页,将其内容写入Page File
(若脏位D=1
) - 将所需页从
Page File
加载到物理内存,更新页表项的存在位和物理页框号
- 当物理内存不足且访问的页未加载(存在位
-
物理内存会被划分为固定大小的块(页框,
Page Frame
),并且进程的虚拟地址空间也会被划分为相同大小的页(Page
),具体是怎么实现虚拟内存与物理内存的动态映射?- 物理内存被划分为页框(如
4KB
大小),每个页框有唯一的物理地址 - 进程的虚拟地址空间被划分为虚拟页,大小与页框相同(如
4KB
) - 页表(
Page Table
)记录虚拟页到物理页框的映射关系,每个进程拥有独立的页表
页目录表(一级页表):存储二级页表的物理基地址和权限标志
二级页表:存储虚拟页到物理页框的映射项(PTE
),每个PTE
包含物理页框号、存在位(Present
)、读写权限(R/W
)等标志 - 若
PTE
的存在位为0
,表示该页未加载到物理内存,会触发缺页中断 - 如果是虚拟页未分配物理页框,需从磁盘(如
Page File
或文件)加载数据到内存,并更新页表项 - 如果虚拟页已分配物理页框,但尚未加载到内存(如共享库已被其他进程加载)。此时仅需将已存在的物理页框与虚拟页关联
- 总结的话,就是:
- 进程启动时,操作系统仅为进程分配虚拟地址空间,但不会立即分配全部物理内存
当进程首次访问位于某个虚拟页的虚拟地址时,其对应的页表项可能处于未分配或未加载状态,此时会触发缺页中断,具体如上述
- 物理内存被划分为页框(如
-
所谓物理内存划分为固定大小的页框,然后由页表保存页表项,一个页表在系统上表现为一个文件吗?
- 物理内存被划分为固定大小的页框(
Page Frame
),而页表(Page Table
)是操作系统用于管理虚拟地址到物理地址映射的核心数据结构 - 页表本身并不表现为文件,而是以内存中的数据结构形式存在
- 但是,当物理内存不足时,操作系统会将不活跃的物理页(页框)换出到磁盘的交换文件(
Page File/Swap Space
),此时页表项中的存在位(Present Bit
)同时也会被清零,标记该页不在内存中 - 访问被换出的页面时,触发缺页中断,操作系统从交换文件加载数据到新分配的物理页框,并更新页表项的存在位和物理页框号
- 物理内存被划分为固定大小的页框(
-
pagefile.sys
存储的是部分不活跃的物理页(页框)的全部数据吧,而不是一个不活跃的物理页一个pagefile.sys
文件?pagefile.sys
是Windows
系统中唯一的虚拟内存页面文件,它存储的是部分不活跃物理页(页框)的全部数据,而非每个不活跃页对应一个单独文件
-
系统中有很多进程同时在运行,虽然通过虚拟内存的分页技术和硬盘的结合,为每个进程分配了
4GB
的进程地址空间(32
位系统,虚拟内存),但是系统的物理内存大小可能就8GB
,它是怎么够用的?- 进程启动时仅分配虚拟地址空间,物理内存的分配是在首次访问页面时触发的(通过缺页中断)
- 虚拟地址空间中大部分区域初始状态为“空闲”或“保留”,只有实际被使用的“已提交”内存才会占用物理资源
- 当物理内存不足时,操作系统会将不活跃的页面换出到硬盘的
pagefile.sys
(Windows
)或交换分区(Linux
) - 多个进程可共享同一物理内存页
例如,Windows
的DLL
文件或Linux
的共享库(如glibc
)只需加载一次,所有调用进程共享其代码段 - 当进程尝试修改共享内存时,系统会创建该页的私有副本,避免直接冲突
本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ X86_64汇编学习记述四08/09
- ♥ 关于多字节和宽字节二12/04
- ♥ Windows 核心编程 _ 线程优先级与关联性07/09
- ♥ Windows消息处理机制04/29
- ♥ Soui二05/18
- ♥ X86_64汇编学习记述三08/08