iOS内存分配:堆、栈、全局区、常量区、代码区

前言

内容参考:

iOS 内存分配 栈、堆、全局区、常量区、代码区

NSString存储管理--NSTaggedPointerString

iOS开发中的内存分配(堆和栈)

分析

iOS内存分配:堆、栈、全局区、常量区、代码区_第1张图片
iOS内存分配.png

1. 代码区

代码区是用来存放函数的二进制代码,也就是,它是可执行程序在内存中的镜像。代码段需要防止在运行时被非法修改,所以只允许读取操作,而不允许写入操作。

2. 全局(静态)区

  • 数据区:数据段用来存放可执行文件中已经初始化的全局变量,也就是用来存放静态分配的变量和全局变量。
  • BSS区:BSS段包含了程序中未初始化的全局变量

3. 常量区

常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量。

4. 堆(heap)区

堆是由程序员分配和释放,用于存放进程运行中被动态分配的内存段。它大小不固定,可动态扩张和缩减。

5. 栈(stack)区

栈是由编译器自动分配释放来管理内存。用户存放程序临时创建的变量、存放函数的参数值、局部变量等。由于栈的先进后出特点,所以特别适合用来做保存/恢复现场的操作。从这个吧意义上,我们可以把栈看做一个临时寄存、交换的内存区。

static 修饰的属性始终保存到常量区。

上述几种内存区域中,数据段、BSS、堆通常都是被连续存储的-内存位置上的连续(并不是堆链式存储的内存区域)。而代码段和栈往往会被独立存放。

栈是向低地址扩展的数据结构,是一块连续的内存区域。堆是向高地址扩展的数据结构,是不连续的内存区域。

内存模型 Heap、Stack

以下内容引用自阮一峰老师的 汇编语言入门教程。

对于堆 heap 和 栈 stack 描述更加详细易懂。

1. 堆 Heap

寄存器只能存放少量的数据,大多数时候, CPU 还要指挥寄存器,直接跟内存交换数据,所以,除了寄存器,还必须了解内存怎么存储数据。

程序运行的时候,操作系统会给它分配一块内存,用来存储程序和运行产生的数据,比如从 0x10000x8000,起始地址是较小的那个地址,结束地址是较大的那个地址。

iOS内存分配:堆、栈、全局区、常量区、代码区_第2张图片
内存示意图

程序运行过程中,对于动态的内存占用请求(比如新建对象),系统就会从预先分配好的那段内存中,划出一部分给用户,具体规则是从起始地址开始划分(实际上,起始地址会有一段静态数据,这里忽略)。举例来说,用户要求得到10个字节内存,那么从起始地址 0x1000 开始给他分配,一直分配到 0x100A,如果在要求得到 22 个字节,那么就分配到 0x1020

iOS内存分配:堆、栈、全局区、常量区、代码区_第3张图片
内存分配

这种因为用户主动请求而划分出来的内存区域,叫做 Heap(堆)。它由起始位置开始,从低位(地址)向高位(地址)增长。 Heap 的一个重要特点就是不会自动消失,必须手动释放,或者由垃圾回收机制来回收。

2. 栈 Stack

除了 Heap 外,其他的内存占用叫做 Stack(栈)。简单来说,Stack 是由于函数运行而临时占用的内存区域。

iOS内存分配:堆、栈、全局区、常量区、代码区_第4张图片
栈内存

请看下面的例子:

int main() {
    int a = 2;
    int b = 3;
}

上面代码中,系统开始执行 main 函数时,会为它在内存里面建立一个 帧(frame),所有的 main 的内部变量(比如 ab)都保存在这个 里面。main 函数执行结束后,该帧就会被回收,释放所有的内部变量,不再占用空间。

iOS内存分配:堆、栈、全局区、常量区、代码区_第5张图片
帧(frame)

如果函数内部调用其他函数,会发生什么情况呢?

int main() {
    int a = 2;
    int b = 3;
    return add_a_and_b(a,b);
}

上面代码中,main 函数调用了 add_a_and_b 函数。执行到这一行的时候,系统也会为 add_a_and_b 新建一个 帧(frame),用来存储它的内部变量。也就是说,此时同时存在两个帧:mainadd_a_and_b。一般来说,调用栈有多少层,就有多少帧。

iOS内存分配:堆、栈、全局区、常量区、代码区_第6张图片
函数嵌套

等到 add_a_and_b 运行结束,它的帧就会被回收,系统会回到刚才 main 函数中断执行的地方,继续往下执行。通过这种机制,就实现了函数的层层调用,并且每一层都能使用自己的本地变量。

所有的帧都存放在 Stack ,由于帧是一层层叠加的,所以 Stack 叫做 。生成新的帧,叫 入栈,英文单词是 push;栈的回收叫 出栈,英文是 popStack 的特点就是,最晚入栈的帧最早出栈(因为最内层的函数调用,最先结束执行),这种叫做 后进先出 的数据结构。每一次函数执行结束,就自动释放一个帧,所有的函数执行结束,整个 Stack 就都释放了。

Stack 是由内存区域的结束地址开始,从高位(地址)向地位(地址)分配。比如,内存区域的结束地址是 0x8000,第一帧假定是 16 字节,那么下一次分配的地址就会从 0x7FF0 开始;第二帧假定需要 64 字节,那么地址就会移到 0x7FB0

iOS内存分配:堆、栈、全局区、常量区、代码区_第7张图片
栈内存地址分配

关于 NSString 的内存分配

NSString存储管理--NSTaggedPointerString 文章中详细说明了问题,我们并不能单纯的通过打印内存地址,来判断数据存储的区域在常量,或者堆区、栈区。

你可能感兴趣的:(iOS内存分配:堆、栈、全局区、常量区、代码区)