戳我
戳我
文章目录
  1. iOS开发的内存分配
    1. RAM ROM
    2. App程序启动
    3. 内存分区
      1. 栈区(stack)
      2. 堆区(heap)
      3. 全局区(static)
      4. 常量区
      5. 代码区
    4. 内存申请后的系统响应
    5. 申请内存后系统的响应
    6. 申请大小的限制
    7. 注意事项

内存分配

iOS开发的内存分配

计算机系统中, 应用程序的数据都保存在内存中, 不同类型的数据, 保存的区域不同.

RAM ROM

RAM: 运行内存, 不能掉电存储.ROM: 存储型内存, 可以掉电存储, 例如内存卡, FLash.
由于RAM类型不具备掉电存储的能力, 所以App程序一般存放在RMO中. RAM的访问速度和价格都高于ROM.

App程序启动

App程序启动, 系统会把开启的那个App程序从Flash或ROM里面拷贝到内存(RAM)中, 然后从内存里面执行代码.
另外一个原因是CPU不能直接从内存卡里面读取指令(需要Flash驱动等等)

内存分区

栈区(stack)

栈区由编译器自动分配并且释放, 存放局部变量, 函数的参数值, 函数跳转地址, 现场保护等.栈是系统数据结构, 对应线程/进程是唯一的. iPhone的栈区大小是512K.

栈空间分配分为静态分配和动态分配两种.

  • 静态分配是由便一起完成, 比如自动变量auto的分配. 动态分配由alloca函数完成.
  • 栈的动态分配无需释放, 系统会自动释放, 没有释放函数. 系统不鼓励栈的动态分配.

关于栈还需要注意:

  • 不需要我们管理栈区变量的内存
  • 栈区地址从高到低分配
  • 先进后出

堆区(heap)

堆区由我们分配(iOS下的alloc)和释放, 如果不释放, 在程序结束时, 可能会由系统回收(iOS的ARC下). 灵活方便, 数据适应面广, 但是因为顺序随意, 所以效率有一定降低.
关于堆还需要注意:

  • 堆区的内存分配使用alloc.
  • 需要程序猿管理.
  • ARC下的内存管理由编译器自动添加retain, release, autorelease等关键字.
  • 堆区的地址由低到高分配.
  • 不同堆分配的内存无法互相互操作(不同App之间内存无法相互管理).
  • 堆空间的分配总是动态的.

全局区(static)

全局区也叫作静态区. 全局变量和静态变量在内存中是放在一起的, 初始化的全局变量和静态变量放在一块区域, 未初始化的全局变量和静态变量放在相邻的另一块区域. 程序结束后由系统释放

int a;//未初始化的
int b = 10;//初始化的

常量区

常量字符串就存放在这里, 程序结束后由系统自己释放.

代码区

代码区也叫作函数区, 存放函数的二进制代码, App的代码. 程序结束后由系统释放

内存申请后的系统响应

1.栈: 存储的函数在执行的时候都会向操作系统索要资源, 栈区就是函数运行时的内训, 栈区中的变量由编译器负责释放和分配, 内存随着函数的运行和结束而分配和释放, 由系统完成. 只要剩余的栈空间大于申请空间, 系统将会为程序提供内存, 否则将报异常提示栈溢出.
2.堆: 操作系统有一个记录空闲地址的链表, 当系统收到程序申请时, 会遍历该链表, 寻找第一个空间大于所申请空间的堆节点, 然后将该节点从空闲节点链表中删除, 并将该节点的空间分配给程序. 由于找到的堆节点大小不一定正合适, 系统会将多余的那部分重新放入空闲链表.

申请内存后系统的响应

  1. 栈: 栈是向低内存扩展的数据结构, 是一块连续的内存, 栈顶地址和栈的最大容量是事先规定好的, 如果申请的空间超过栈的剩余空间, 将会提示overflow.

  2. 堆: 操作系统有一个记录空闲内存地址的链表. 当系统收到申请时, 会遍历该链表, 寻找第一个空间大于所申请空间的堆结点, 然后将该结点从链表中删除, 并将该结点的空间分配给程序. 由于找到的堆结点不一定和申请的大小刚好一致, 系统会将多余的那一部分重新放回到链表中去. 堆是向高地址扩展的数据结构, 是不连续的内存区域. 这是由于系统是用链表来存储的空闲内存地址, 自然是不连续的, 而链表的遍历方向是由低地址向高地址. 堆的大小受限于计算机系统中有效的虚拟内存.

申请大小的限制

  1. 栈: 栈是向低地址扩展的数据结构, 是一块儿连续的内存区域. 栈顶的地址和栈的最大容量是系统预先规定好的, 栈的大小是2M(也有的说是1M, 总之是一个编译时就确定的常数), 如果申请的空间超过栈的剩余空间时, 将提示overflow. 因此, 能从栈获得的空间较小.

  2. 堆: 堆是向高地址扩展的数据结构, 是不连续的内存区域. 这是由于系统是用链表来存储空闲的内存地址的, 自然是不连续的, 而链表的遍历是由低地址到高地址. 堆的大小受限于计算机系统中有效的虚拟内存. 堆获得的空间比较灵活, 也比较大.

注意事项

如图所示: 代码区的地址最低, 栈区最高. 但是区与区之间的地址不连续.
内存地址示意图

  • 栈:由系统自动分配, 速度较快, 不会产生内存碎片. 堆:是由alloc分配的内存, 速度比较慢, 而且容易产生内存碎片, 不过用起来最方便.
  • 在iOS中, 堆区的内存是程序间共享, 堆区的内存分配是系统负责的.
  • 系统使用一个链表来维护已分配的内存空间(仅仅记录, 不管理具体的内容).
  • 变量使用结束后, 需要释放内存, OC中是当引用计数==0, 就说明没有任何变量使用这块空间, 系统将直接收回.
  • 当一个app启动时, 代码区, 常量区, 全局区的大小实际已经固定, 因此指向这些区域的内存不会产生崩溃性错误. 堆区和栈区的内存是时刻变化的, 使用一个已经被释放的内存, 很容易产生野指针崩溃.
  • 栈由系统自动分配, 速度快, 不会产生内存碎片, 先进后出.
  • 堆由alloc分配内存, 速度慢, 而且容易产生碎片, FIFO, 不过使用起来方便.

参考文献:
1.iOS程序中的内存分配
2.深入浅出-iOS内存分配