0x00 开篇
关于 trait 的相关文章暂告一段落,本篇文章将向大家介绍下一个计算机的通用概念——内存。如果你是计算机相关专业的读者或者对内存比较了解,那可以略过本篇文章。
0x01 物理内存与虚拟内存
内存是计算机一个重要的概念,不同的语言对内存的管理不同,有手动管理内存和自动管理内存的两种方式,这也就导致不同语言直接的性能不同。早期的计算机程序都是直接跑在物理内存上的,这就要求程序大小不能超过物理内存的上限。现代计算机为提高CPU使用率、并发执行多个程序、隔离程序间内存地址、打破物理内存上限,采用虚拟内存机制来运行程序。
物理内存就是我们常说的内存条。而虚拟内存仅仅是一个概念。对于任何一个程序来说,它包含了程序运行时可用的内存空间总和,包括共享的、非共享的、物理内存中的、存在分页中的等等。简单点说:虚拟内存映射 = 物理空间 + 硬盘空间 + 未使用的映射。
虚拟内存大小组成的地址空间就是虚拟内存空间,虚拟内存空间是有地址空间的。这就引出另一个概念——虚拟地址空间。虚拟地址空间是线性的,我们所接触的地址都是虚拟地址。虚拟地址空间又被划分为内核空间和用户空间。以 4G 内存为例,在 Windows 32
位操作系统中默认内核空间和用户空间的比例是1:1
(2GB的内核空间,2GB的用户空间),而在32位Linux
系统中默认的比例是1:3
(1GB的内核空间,3GB的用户空间)寻址范围是 0x00000000 - 0xFFFFFFFF。讲了这么多,下面直接看图吧。
细心的读者可能发现了,Windows 的内核空间有 64KB 没有使用。从 0x7FFF0000-0x7FFFFFFF:这个64KB是禁止访问的,因为它挨着内核区域,防止内核区域被覆盖,以破坏内核的正确性和完整性,如果试图改写此区域,会产生异常。
最后我们以Linux
系统为例,看下 Linux 系统下的虚拟地址空间示意图。Linux 对进程地址空间有个标准布局,地址空间中由各个不同的内存段组成,主要的内存段如下:
- 程序段 (Text Segment):可执行文件代码的内存映射
- 数据段 (Data Segment):可执行文件的已初始化全局变量的内存映射
- BSS段 (BSS Segment):未初始化的全局变量或者静态变量(用零页初始化)
- 堆区 (Heap) : 存储动态内存分配,匿名的内存映射
- 栈区 (Stack) : 进程用户空间栈,由编译器自动分配释放,存放函数的参数值、局部变量的值等
- 映射段(Memory Mapping Segment):任何内存映射文件
0x02 栈内存
如果你了解过数据结构,那你肯定听说过“栈”,它拥有后进先出的特性。栈内存则拥同栈数据结构一样的特性,支持入栈和出栈。下图是栈数据结构入栈和出栈的示意图。
当有数据入栈时,栈顶的地址会变小,反之数据出栈时,栈顶的地址会增大。栈内存的主要作用是在程序运行时保存函数调用所维护的一些信息。有关栈内存相关的知识点,大家可以通过搜索或者其他书籍了解下,我就不再展开讲解了。
0x03 堆内存
同样,“堆”也存在于数据结构中,它是一颗完全二叉树(有关数据结构相关的知识,后面也会有一系列的文章,这里不再多余赘述)。我们主要了解堆内存。堆是一个大块的内存空间,程序申请堆空间时是大小不一,无序且不连续。这就出现一个如何管理堆内存的问题。Rust编译器目前自带两个默认分配器:alloc_system
和 alloc_jemalloc
。从 Rust 1.32.0 版本开始,把分配器默认切换为 alloc_system
,但开发者仍然可以从 crate 使用到 alloc_jemalloc
。过去Rust使用alloc_jemalloc
,虽然普遍认为alloc_jemalloc
的性能比较好,但随着时间发展,情况已经改变。
0x04 堆内存和栈内存的区别
其实物理内存并没有区分堆和栈,堆内存和栈内存只存在于虚拟地址空间中。
- 生命周期
在栈内存中保存的数据往往生命周期都比较短。比如某个函数执行结束后,内存会释放。但是如果想要长久的保存数据,这时候需要使用堆。 - 是否能够确定数据大小
在写代码时,如果可以知道某种数据类型的大小,我们可以将其分配到栈中,如固定大小的基本数据类型和固定大小的数组。但是如 果是动态数组则需求分配到堆内存。
- 速度
数据在入栈出栈操作要比在堆上分配内存或者在堆上读取数据的速度快。因为入栈出栈操作,位置总是在栈顶。而在堆上操作时要做很多工作。
开发过程中,我们只能通过指针来掌握已分配的堆内存,如果指针指向的堆内存已经释放,但是指针还没有释放(野指针),这就会对我们的程序造成严重的影响。
0x05 小结
本篇文章介绍了很多概念,但是我也只是简单写一下,如果你对内存方面的知识比较感兴趣,可以多多查找资料。我相信很多人接触 Rust 是因为很多文章都在说 Rust 很安全。那么 Rust 为何安全?Rust 为什么能保证内存安全呢?Rust 是如何管理内存的?和其它主流语言的区别是什么呢?有关这些疑问,接下来的文章将会全部给你答案。