内存是如何分配的
在 Linux 中,内存管理是由 内存管理子系统(Memory Management Subsystem) 负责的,涉及多个层次的分配机制,包括 物理内存管理、虚拟内存管理、页管理、缓存管理 等。下面是 Linux 内存分配的主要机制:
1. 物理内存管理
Linux 物理内存管理基于 分页(Paging) 机制,而不是分段(Segmentation)。主要机制包括:
1.1 页(Page)和页帧(Page Frame)
- Linux 默认使用 4KB 页(也支持大页,如 HugePages)。
- 物理内存被划分为固定大小的页帧(Page Frame),每个页帧通常是 4KB。
- 页帧由 伙伴系统(Buddy System) 进行管理。
1.2 伙伴系统(Buddy System)
- 伙伴系统用于管理 连续的空闲内存块,提高分配和合并效率。
- 它将物理内存分成 2^n 大小的块,并尽可能合并相邻的空闲块,以减少内存碎片。
2. 虚拟内存管理
Linux 采用 虚拟地址到物理地址映射(VA → PA),并使用 页表(Page Table) 来管理虚拟地址。
2.1 地址空间
- 用户态地址空间(User Space):通常是 0x0000000000000000 - 0x00007FFFFFFFFFFF(48-bit)。
- 内核态地址空间(Kernel Space):通常是 0xFFFF800000000000 - 0xFFFFFFFFFFFFFFFF(高地址)。
2.2 页表(Page Table)
Linux 采用 多级页表(4 级/5 级) 来减少内存开销: - PGD(Page Global Directory) - PUD(Page Upper Directory) - PMD(Page Middle Directory) - PTE(Page Table Entry)
映射关系:虚拟地址 → 物理地址
3. 内存分配机制
3.1 用户态内存分配
3.1.1 malloc()
& brk()/mmap()
malloc()
申请内存时,可能调用brk()
或mmap()
:brk()
:扩展 堆(Heap)。mmap()
:映射大块内存到虚拟地址空间(用于大内存分配)。
3.1.2 进程堆栈
- 栈(Stack):自动管理,函数调用时分配局部变量,递归深度受栈大小限制。
- 堆(Heap):由
malloc()
申请,由free()
释放。
3.2 内核态内存分配
3.2.1 kmalloc()
- 内核的
malloc()
,用于分配小块物理内存(通常小于 PAGE_SIZE)。 - 使用 Slab 分配器 进行管理(减少碎片)。
3.2.2 vmalloc()
- 分配 连续的虚拟内存(但可能是物理上不连续)。
- 用于分配大块内存(超过
PAGE_SIZE
)。
3.2.3 alloc_pages()
- 直接调用 伙伴系统 分配 2^n 个页。
- 适用于驱动或 DMA 设备的物理页分配。
4. 内存回收
Linux 使用 页回收(Page Reclaim) 和 交换(Swap) 机制来优化内存使用:
4.1 页回收(Page Reclaim)
- 通过 LRU(Least Recently Used) 机制回收不常使用的页。
- 可回收的页:
- 文件页(File-backed Pages):可以丢弃,因为可从磁盘重新加载。
- 匿名页(Anonymous Pages):只能写入 Swap 才能回收。
4.2 交换(Swap)
- 当物理内存不足时,Linux 会将匿名页移到 Swap 分区或 Swap 文件中。
- Swap 机制有:
- swappiness(控制 Swap 频率,0 表示尽量不用 Swap,100 表示积极使用 Swap)。
- OOM Killer(当 Swap 也不足时,Linux 可能会杀掉进程)。
5. 常见的内存管理工具
free -h
:查看内存使用情况(包括 Swap)。top
/htop
:监控进程的内存占用。vmstat
:显示内存分配、Swap 统计等信息。slabtop
:查看 Slab 分配器的使用情况。numactl --hardware
:查看 NUMA 结构的内存分布。
6. 总结
机制 | 作用 |
---|---|
伙伴系统 | 管理物理页帧,减少碎片 |
Slab 分配器 | 管理小块内存,提高缓存效率 |
页表管理 | 实现虚拟地址到物理地址映射 |
LRU 回收 | 回收不常用的内存页 |
Swap | 交换不活跃的匿名页到磁盘 |