01-主题|内存管理@iOS-内存五大分区

本文介绍 iOS 进程内存中的五大分区:栈区、堆区、全局区(静态区)、常量区、代码区。涵盖各区的定义、存储内容、地址特征、分配与释放时机,以及栈帧与堆栈溢出等要点,并给出验证示例与对比小结。


目录


一、总览

在 iOS 中,进程内存主要分为 栈区、堆区、全局区、常量区、代码区 五大区域。各区的地址范围、分配时机与管理者不同,对应不同的使用场景与注意事项。

1.1 五大区速览表

分区 常见地址前缀(iOS) 分配时机 分配/释放者 主要存放
栈区 0x7... 运行时 编译器自动分配与释放 局部变量、函数参数、返回地址等
堆区 0x6... 运行时 程序员分配(ARC/MRC 管理释放) alloc/new 对象、malloc 等动态分配
全局区 0x1... 编译时 程序结束后系统释放 .bss:未初始化全局/静态变量;.data:已初始化全局/静态变量
常量区 0x1...(只读) 编译时 程序结束后系统释放 字符串常量等只读数据(.rodata)
代码区 --- 编译时 程序结束后系统释放 程序机器码(.text)

说明:地址前缀因架构与系统版本可能略有差异,实际以运行打印为准;全局区与常量区在部分环境下可能同属 0x1 段,通过只读/可写与段名区分。

1.2 内存布局示意(由高地址到低地址)

flowchart TB subgraph 高地址 A[栈区 Stack
0x7... 向下增长] end subgraph 中间 B[堆区 Heap
0x6... 向上增长] end subgraph 低地址 C[全局区 .bss / .data
0x1...] D[常量区 .rodata] E[代码区 .text] end A --> B B --> C C --> D D --> E

二、栈区(Stack)

2.1 定义

  • 栈是系统级数据结构 ,与进程/线程一一对应(每个线程有独立栈空间)。
  • 栈是向低地址扩展 的连续内存区域,遵循先进后出(FILO)
  • 在 iOS 中栈地址通常以 0x7 开头。
  • 栈空间在运行时由系统分配。

2.2 存储内容

栈区由编译器自动分配并释放,主要存放:

  • 局部变量(含基本类型、指针变量等)。
  • 函数参数 ,包括隐藏参数(如 Objective-C 的 id selfSEL _cmd)。
  • 函数调用的返回地址 、栈帧信息等(详见 八、函数栈与栈帧)。

2.3 特点与约束

项目 说明
优点 自动分配释放,无内存碎片,访问高效
缺点 容量有限,数据生命周期与作用域绑定,不够灵活
栈大小 主线程栈一般为 1MB ,子线程一般为 512KB ;macOS 上主线程栈约 8MB 。详见 Threading Programming Guide

三、堆区(Heap)

3.1 定义

  • 堆是向高地址扩展不连续内存区域,内部常以链表等结构管理空闲块。
  • 在 iOS 中堆地址通常以 0x6 开头。
  • 堆空间在运行时 由程序动态申请,大小灵活。

3.2 存储内容

堆区由程序员(或 ARC)分配与释放,主要存放:

  • Objective-Callocnewcopy 等创建的对象
  • C 语言malloccallocrealloc 分配的内存,需配套 free 释放。

3.3 访问方式

访问堆上对象时,通常先通过栈上的指针 (或寄存器)得到对象地址,再通过该地址访问堆内存。即:栈存「指针」,堆存「实际对象数据」。

3.4 特点

项目 说明
优点 容量大、生命周期可灵活控制,适应面广。
缺点 需管理释放(MRC 手动 / ARC 自动),分配与释放比栈慢,易产生内存碎片

四、全局区(静态区:.bss 与 .data)

4.1 定义

  • 全局区在编译时 即确定布局,在 iOS 中地址常以 0x1 开头。
  • 程序运行期间该区域一直存在,程序结束后由系统释放

4.2 存储内容

内容
.bss 未初始化的全局变量、静态变量(系统会将其初始化为 0)。
.data 已初始化的全局变量、静态变量。

全局变量静态变量 :全局变量在全局可见;静态变量static 修饰,包括静态局部变量(作用域限于函数内、生命周期贯穿程序)和静态全局变量(本文件内可见)。


五、常量区(.rodata)

5.1 定义

  • 常量区在编译时 分配,只读,程序结束后由系统释放。
  • 对应段名常为 .rodata(read-only data)。

5.2 存储内容

  • 字符串常量 (如 @"hello"):在程序运行前即分配好,只读,可被多处引用。
  • 其他只读常量(如 const 全局、字面量等,视实现而定)。

已在程序中使用的、且无其他指针指向的字符串常量,会在运行前就分配在常量区,以便复用与节省空间。


六、代码区(.text)

6.1 定义与内容

  • 代码区编译时 分配,主要存放程序的机器码 (指令),对应段一般为 .text
  • 代码被编译成二进制后加载进内存,通常为只读,程序结束后由系统释放。

七、五大区对比与验证

7.1 对比小结表

维度 栈区 堆区 全局区 常量区 代码区
地址特征 0x7,向下增长 0x6,向上增长 0x1 0x1(只读) 低地址
分配时机 运行时 运行时 编译时 编译时 编译时
谁释放 编译器/系统 程序员/ARC 系统 系统 系统
典型内容 局部变量、参数 对象、malloc 全局/静态变量 字符串常量等 机器码

7.2 验证代码示例

通过打印变量与指针的地址,可直观看到各区的分布:

objc 复制代码
- (void)test {
    NSInteger i = 123;
    NSLog(@"i 的地址(栈上局部变量): %p", &i);           // 预期 0x7...

    NSString *string = @"CJL";
    NSLog(@"string 对象地址(常量区): %p", string);      // 预期 0x1...
    NSLog(@"&string 指针地址(栈): %p", &string);       // 预期 0x7...

    NSObject *obj = [[NSObject alloc] init];
    NSLog(@"obj 对象地址(堆): %p", obj);               // 预期 0x6...
    NSLog(@"&obj 指针地址(栈): %p", &obj);            // 预期 0x7...
}

7.3 结果解读

打印项 含义 预期分区
&i 局部变量 i 的地址 栈区(0x7...)
string 字符串对象首地址 常量区(0x1...)
&string 指针变量 string 的地址 栈区(0x7...)
obj alloc 创建的对象首地址 堆区(0x6...)
&obj 指针变量 obj 的地址 栈区(0x7...)

结论:指针变量本身 在栈(或寄存器),对象/常量数据在堆或常量区;通过栈上的指针访问堆或常量区。


八、函数栈与栈帧

8.1 概念

  • 函数栈 即线程使用的栈区,从高地址向低地址增长,与堆区相对。
  • 栈帧(Stack Frame)一次未完成调用的函数 所占用的一块连续栈空间,用于存放该次调用的参数、局部变量、返回地址等。

8.2 线程与栈帧关系

  • 每个线程 拥有独立的栈 ;该线程内所有函数调用共享这份栈空间。
  • 每发生一次函数调用 ,就生成一个栈帧 并压栈;函数返回时,对应栈帧出栈并释放。
  • 当前线程的栈 = 当前所有未返回函数的栈帧组成的序列。

8.3 ARM 栈帧布局要点

  • 栈底 在高地址,栈向低地址增长。
  • FP(Frame Pointer):栈基址,指向当前函数栈帧的起始位置。
  • SP(Stack Pointer):栈顶指针,随压栈/出栈移动。
  • 压栈顺序(典型):返回地址、保存的 FP、局部变量、临时变量等;若再调用子函数,会先为子函数参数预留空间。

8.4 栈帧变化示例

objc 复制代码
int Add(int x, int y) {
    int z = 0;
    z = x + y;
    return z;
}

int main() {
    int a = 10;
    int b = 20;
    int ret = Add(a, b);
    return 0;
}

执行过程简述:

  1. main 入栈:为 ab 等分配栈帧,调用 Add 前将参数压栈。
  2. Add 入栈:为 xyz 等分配新栈帧,SP 下移。
  3. Add 返回:Add 的栈帧出栈,SP 回到 main 的栈帧,返回值通过约定(寄存器或栈)传回 main。

九、堆栈溢出与预防

9.1 原因简述

  • 栈溢出 :栈空间有限,递归过深局部变量过大会导致栈溢出。
  • 堆溢出 :堆虽大但非无限;过多或过大的动态分配且不释放,会导致堆空间耗尽或 OOM。

9.2 预防要点

类型 建议
避免层次过深的递归;控制局部变量数量和大小;必要时可考虑改为迭代或增大线程栈(系统 API,谨慎使用)。
避免分配过大的单块对象;及时释放不再使用的对象;注意循环引用与泄漏(ARC 下也要避免强引用环)。

延伸阅读


参考文献

相关推荐
没有故事的Zhang同学1 小时前
03-主题|事件响应者链@iOS-响应者链与nextResponder详解
程序员
三小河2 小时前
VS Code 集成 claude-code 教程:告别海外限制,无缝对接国内大模型
前端·程序员
用户60572374873082 小时前
AI 编码助手的规范驱动开发 - OpenSpec 初探
前端·后端·程序员
没有故事的Zhang同学2 小时前
02-主题|事件响应者链@iOS-hitTest与事件传递详解
程序员
没有故事的Zhang同学5 小时前
03-超级App软件平台@路由规则设计-【Universal-Links】与【App-Links详解】
程序员
没有故事的Zhang同学6 小时前
01-超级App软件平台@路由规则设计-【总纲】
程序员
没有故事的Zhang同学6 小时前
04-超级App软件平台@路由规则设计-【组件化路由框架详解】
程序员
没有故事的Zhang同学6 小时前
07-超级App软件平台@路由规则设计-【通用路由管理组件设计】
程序员