1. 物理内存 (Physical Memory)
物理内存是计算机系统中的实际硬件存储器,通常指的是随机存取存储器(RAM)。它直接为运行中的程序和操作系统提供存储空间。物理内存的大小是固定的,由硬件决定。操作系统通过一定的策略来管理物理内存的使用。
关键点:
- 物理内存是有限的硬件资源,且不同进程共享这一资源。
- 操作系统使用一定的算法来分配和管理物理内存,确保所有程序有足够的资源运行。
- 由于物理内存有限,操作系统需要虚拟内存技术来拓展其可用性。
2. 虚拟内存 (Virtual Memory)
虚拟内存是一种计算机系统内存管理技术,通过虚拟化物理内存,给每个进程提供一个完整的、连续的内存地址空间。虚拟内存的概念使得程序可以假设自己拥有独立的内存空间,而实际上,它们共享相同的物理内存资源。
操作系统通过将内存划分为页(pages),使用页表将虚拟地址映射到物理地址。 当物理内存不足时,虚拟内存还可以通过将不常用的内存页面暂时存放到硬盘上的交换区(swap space),以实现内存的扩展。 虚拟内存技术让程序可以使用比物理内存更多的内存,并且为每个进程提供一个隔离的内存空间,增强了安全性和稳定性。
关键点:
- 虚拟内存使每个进程看似拥有独立且连续的内存空间。
- 通过分页(paging)或分段(segmentation)机制,虚拟地址可以映射到物理内存地址或磁盘上的交换空间。
3. 用户态内存映射 (User-Space Memory Mapping)
用户态内存映射指的是在用户空间(用户态)中,进程如何通过虚拟地址访问实际的物理内存。通常,用户态程序不直接操作物理内存,而是通过虚拟内存进行访问,操作系统负责将用户态虚拟地址映射到物理地址。
- 用户态的程序不能直接访问内核空间内存,防止破坏操作系统的稳定性和安全性。
- 用户态进程通过系统调用(如 mmap())可以将文件或设备映射到进程的地址空间,这样可以通过内存直接访问文件内容,提高性能。
- 内存映射文件时,操作系统不会立即分配物理内存,而是按需加载到物理内存,节省资源。
关键点:
用户态程序通过虚拟内存间接访问物理内存。
用户态程序可以通过内存映射将文件或设备映射到地址空间,实现更高效的文件读写。
4. 内核态内存映射 (Kernel-Space Memory Mapping)
内核态内存映射指的是操作系统内核在管理系统时如何映射内存。内核态程序(如驱动程序和操作系统自身)拥有直接访问物理内存的权限。内核的职责是为用户态进程管理内存,并为它们提供必要的映射。
- 内核态代码运行在特权级较高的CPU模式下,拥有完全的内存访问权限。
- 内核在用户态进程请求内存时,会为其分配合适的虚拟内存并映射到物理内存,或者将部分内存分页到磁盘。
- 内核内存通常是分页的,但有些关键区域可能会使用大页或者不分页,以提高效率。
关键点:
- 内核态程序直接操作物理内存,负责所有内存的分配和管理。
- 内核态内存映射管理用户态的内存请求,并提供保护和隔离。
5. 进程空间管理 (Process Space Management)
进程空间管理指的是操作系统如何为每个运行中的进程分配和管理它的虚拟内存空间。每个进程都有一个独立的虚拟地址空间,这些虚拟地址通过操作系统映射到物理内存或磁盘中的交换空间。
进程的虚拟地址空间一般分为以下几部分:
- 代码段:存储程序的可执行代码。
- 数据段:存储已初始化的全局变量和静态变量。
- 堆:用于动态分配内存,进程可以在运行时申请内存。
- 栈:用于函数调用时保存局部变量和调用链。
关键点:
- 进程空间管理确保每个进程都有独立的、受保护的内存空间。
- 堆和栈的管理使得程序可以高效利用内存,并在必要时释放或分配资源。
操作系统通过以下机制管理进程空间:
- 分页机制:将进程的虚拟内存划分为固定大小的页面,并将这些页面映射到物理内存或交换空间。
- 分段机制:将虚拟内存划分为段(如代码段、数据段、堆和栈),并进行段内管理。
- 虚拟内存保护:通过不同的权限级别,防止进程互相访问彼此的内存空间,确保安全性和稳定性。
尽管编程语言有所不同,大多数编程语言编写的程序在底层通常会划分成以下基本的内存区域:
代码段(Text Segment)
存储应用程序的可执行指令。 这是只读区域,防止程序修改自身代码。 数据段(Data Segment):
已初始化的全局变量和静态变量存储在此区域。 数据段通常包括已初始化数据段(例如已赋值的全局变量)和未初始化数据段(如未赋值的全局变量,通常称为 BSS 段)。
堆(Heap)
动态内存分配区域,用于在运行时通过 malloc、new 等方式分配的内存。 堆的大小在运行时动态变化,由程序管理内存的分配和释放。
栈(Stack)
存储函数调用时的局部变量、函数参数和返回地址。栈空间是系统自动分配和释放的,大小受限且是连续增长和收缩的。
不同编程语言对内存的管理差异
- C/C++
- 手动内存管理:C 和 C++ 程序员需要显式地管理内存(如通过 malloc/free 或 new/delete)。
- 内存划分经典:堆和栈的分配非常明确,程序员需要直接处理内存泄漏和碎片化等问题。
- 没有垃圾回收:内存管理完全依赖程序员,不存在自动的内存回收机制。
- Java
- JVM 管理内存:Java 程序运行在 Java 虚拟机(JVM)上,内存划分包括堆、栈、方法区(存储类元数据)、本地方法栈和 PC 寄存器。
- 垃圾回收机制:Java 的堆内存由 JVM 管理,并由垃圾回收器自动回收不再使用的对象。
- 栈用于线程:每个线程有自己的栈,存储局部变量和方法调用链。
- Python
- 动态内存分配:Python 所有变量都是对象,所有对象都在堆上分配内存。
- 内存管理由解释器负责:Python 解释器(CPython)会使用引用计数和垃圾回收(GC)机制管理内存。
- 栈和帧:Python 的函数调用栈由帧(frame)构成,帧存储局部变量、参数等。
- Go
- 栈自动扩展:Go 的栈是可以自动扩展的,初始大小很小,但随着需要会动态增长。
- 垃圾回收:Go 有内置的垃圾回收机制,自动管理堆内存。
- 并发友好的内存管理:Go 的内存管理优化了多线程并发性能,特别是 goroutines 的轻量级栈。
- Rust
- 所有权模型:Rust 通过所有权(ownership)、借用(borrowing)和生命周期(lifetimes)来管理内存,避免了手动内存管理,同时避免了垃圾回收的开销。
- 无垃圾回收:Rust 在编译时检查内存使用,确保内存安全,避免传统的垃圾回收机制。
- 栈和堆的明确定义:值默认分配在栈上,只有需要动态分配的对象(如通过 Box、Rc 等)才会在堆上。