存储组织
从编译器编写者的角度来看,正在执行的目标程序在它自己的逻辑地址空间内运行,其中每个程序值都在这个空间有一个地址。
对这个逻辑地址空间的管理和组织是由编译器、操作系统、目标机共同完成的。
操作系统将逻辑地址映射为物理地址,而物理地址对整个内存空间编制。
- 运行时刻内存划分为代码区和数据区的典型方式
代码区 |
---|
静态区 |
堆区 |
↓ 空闲内存 ↑ |
栈区 |
一个目标程序在逻辑地址空间的运行时刻映像(image)包含数据区和代码区。
- 内存对齐
数据对象的存储布局受目标机器的寻址约束影响很大。在很多机器中执行整数加法的指令可能要求整数是对齐的,也就是说这些数必须放在能被4整除的地址上。
在C语言或类似语言中,如果有一个10字符的数组,编译器可能为了对齐而给它分配12个字节,其余两个未使用的字节称为补白(padding)。
如果空间比较紧张,编译器可能会压缩数据以消除补白。但是,在运行时刻可能需要额外的指令来定位被压缩的数据,使得机器处理这些数据时,就好像它们是对齐的。
- 代码区
生成的目标代码的大小在编译时刻就已经固定下来了,因此编译器可以将可执行代码放在一个静态确定的区域:代码区。这个区通常位于存储的底端。
- 静态和动态
两个形容词静态(static)和动态(dynamic)分别表示编译时刻和运行时刻。
如果编译器认为只需要通过观察程序文本即可做出某个存储分配决定,而不需要观察该程序在运行时做了什么,我们就认为这个存储分配决定是静态的。
反过来,如果只有在程序运行时才能做出决定,那么这个决定就是动态的。
- 静态区
程序的某些数据对象可以在编译时刻知道,它们可以被放置在另一个称为静态区的区域中,该区域可以被静态确定。
放置在这个区域的数据对象包括全局常量和编译器产生的数据,比如用于GC的信息等。
之所以要将尽可能多的数据对象进行静态分配,是因为这些对象的地址可以被编译到目标代码中。
- 动态区:栈和堆
为了将运行时刻的空间利用率最大化,另外两个区域-堆和栈被放置在剩余地址空间的相对两端。
这些区域是动态的,它们的大小会随着程序运行而改变。这两个区域根据需要向对方增长。
-
栈区用来存放称为活动记录的数据结构,这些活动记录在函数调用过程中生成。
-
很多程序设计语言支持程序员通过程序控制人工分配和回收数据对象。例如C语言中的
malloc
和free
可以用来获取和释放任意存储块。堆区用来管理这种具有长生命周期的数据。
(END)