IOS开发基础——内存的基础探究(堆,栈)

2,444 阅读5分钟

小记

  • 静态存储区(全局存储区)
  • 堆区
  • 栈区
  • 代码区
  • 文字常量区

内存分区图:

内存分区图
代码区存放于低地址,栈区存放于高地址。区与区之间并不是连续的。

app启动后,代码区,文字常量区,全局存储区大小固定,指向这些区的指针不会产生崩溃性的错误。而堆区和栈区是时时刻刻变化的(堆的创建销毁,栈的弹入弹出),当使用一个指针指向这两个区的内存时,要注意内存是否已释放,指向已经释放的内存会产生野指针。

静态存储区(全局存储区)

内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。它主要存放静态数据、全局数据和常量。

全局变量、静态变量会存储在此区域。事实上全局变量也是静态的,因此,也叫全局静态存储区。

存储方式:

  • 初始化的全局变量跟静态变量放在一片区域(数据区)
  • 未初始化的全局变量与静态变量放在相邻的另一片区域(BSS区)

程序结束后由系统释放。

文字常量区

在程序中使用的常量存储在此区域(常量字符串就是放在这里的)。程序结束后,由系统释放。在程序中使用的常量,都会到文字常量区获取。

代码区

存放函数体的二进制代码,运行程序就是执行代码,代码要执行就要加载进内存。

堆区

简述

亦称动态内存分配。程序在运行的时候用malloc或new申请任意大小的内存,程序员自己负责在适当的时候用free或delete释放内存。动态内存的生存期可以由我们决定,如果我们不释放内存,程序将在最后才释放掉动态内存(如果某动态内存不再使用,需要将其释放掉,否则认为发生了内存泄漏现象)。

空间分配

动态分配和回收,不能静态分配(堆区的地址是从低到高分配)。

分配步骤:

  • 操作系统有一个记录空闲内存地址的链表
  • 当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序
  • 由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中

大小限制:

堆是向高地址扩展的数据结构,的内存区域由于系统是用链表来存储的空闲内存地址的,是不连续,而链表的遍历方向是由低地址向高地址。堆的大小受限于系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。

特点

在堆中我们不是按照进入堆的先后顺序来提取元素,而是根据元素的优先级来提取元素,总体来说,堆是一种数据结构,它的插入和删除的操作,要根据优先级来决定,同时堆也有如下几个特性:

  1. 任意节点的优先级不小于它的子节点
  2. 任意节点的值都小于或者等于它的子节点
  3. 主要操作是插入和删除它的最小元素(元素值本身就是优先级的键值,小元素享有高优先级) (每次取出优先级最高的元素,即是让堆的最上层参与者退出堆)

由上面可看出堆的优点是灵活方便,数据适应面广泛,但是效率有一定降低。[顺序随意]

栈区

简述

在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。存储的为非静态的局部变量,例如:函数参数,在函数中生命的对象的指针等。当系统的栈区大小不够分配时, 系统会提示栈溢出。

空间分配

分配方式:(栈区地址从高到低分配),静态分配(例如:局部变量的分配)和动态分配(例如,alloc函数进行分配)都由系统完成,不需要手动管理

分配步骤:

  • 存储每一个函数在执行的时候都会向操作系统索要资源,栈区就是函数运行时的内存。
  • 栈区中的变量由编译器负责分配和释放,内存随着函数的运行分配,随着函数的结束而释放,由系统自动完成。

大小限制:

栈是向低地址扩展的数据结构,是一块连续的内存的区域。是栈顶的地址和栈的最大容量是系统预先规定好的 ,如果申请的空间超过栈的剩余空间时,将提示内存溢出。因此,能从栈获得的空间较小。

特点

只允许在链接串列或者阵列的一段(一般为栈顶)进行加入数据(push)或者输出数据(pop)。由于栈只允许在一端进行操作,按照先进后出的原则来进行。 优点是快速高效,缺点时有限制,数据不灵活。[先进后出]

栈的三个操作:

  1. Pop:取出栈中最上层的元素
  2. top:查看栈顶元素
  3. push:将一个新的元素放在栈的最上层。