美文网首页你好 苹果iOS
iOS内存分配:堆、栈、全局区、常量区、代码区

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

作者: 四月_Hsu | 来源:发表于2019-10-31 17:24 被阅读0次

    前言

    内容参考:

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

    NSString存储管理--NSTaggedPointerString

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

    分析

    iOS内存分配.png

    1. 代码区

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

    2. 全局(静态)区

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

    3. 常量区

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

    4. 堆(heap)区

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

    5. 栈(stack)区

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

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

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

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

    内存模型 Heap、Stack

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

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

    1. 堆 Heap

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

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

    内存示意图

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

    内存分配

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

    2. 栈 Stack

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

    栈内存

    请看下面的例子:

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

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

    帧(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。一般来说,调用栈有多少层,就有多少帧。

    函数嵌套

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

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

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

    栈内存地址分配

    关于 NSString 的内存分配

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

    相关文章

      网友评论

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

        本文链接:https://www.haomeiwen.com/subject/ltitbctx.html