2-2 iOS 内存管理,栈,堆,BSS段,数据段,代码段,野指针,僵尸对象

本文详细介绍了内存管理中的六大区域,包括栈区、堆区、BSS段、数据段、代码段和常量区,并解释了各自的特点及应用场景。重点区分了栈区与堆区的不同之处,以及如何在Objective-C中正确地管理对象的内存。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

内存管理,拆开讲就是对如何将数据存储到内存中,如何释放内存中的数据,什么时候释放。

   内存中的六大区域

     

     内存分为5个区域,分别指的是----->栈区/堆区/BSS/数据段/代码段

     

     栈:存储局部变量,当其作用域执行完毕之后,就会被系统立即收回

     

     堆:存储OC对象,手动申请的字节空间,需要调用free来释放

     

     BSS段:未初始化的全局变量和静态变量,一旦初始化就会从BSS段中回收掉,转存到数据段中

     

     数据段:存储已经初始化的全局变量和静态变量,以及常量数据,直到结束程序时才会被立即收回

     

     常量区:存放常量字符串,程序结束后由系统释放


     代码段:存放函数的二进制代码,直到结束程序时才会被立即收回

     

     (以上摘自文档)

     

     就我而言,经常搞混堆区,栈区的区别。在此做一个总结。

     栈区:向低字节扩展的数据结构,是一块连续的内存区域,栈顶的地址和栈的容量(iOS中是1M)是系统预先规定好的。栈的内存管理由编译器自动管理,不需要我们操心。栈是FILO的队列,一一对应,不存在出现堆会出现的碎片问题。栈的分配方式:静态分配和动态分配。静态分配由编译器完成,比如局部变量的分配。动态分配由alloc函数进行分配。栈的动态分配也是由编译器释放,不需要我们手动实现。一个线程会分配一个栈。

     

    什么时候会通过栈区来分配?

     NSObject *obj = [[NSObject alloc] init];

     系统会在栈区存储obj对象的指针变量,指向堆区存储的obj对象。通过alloc函数,在堆区开辟一块内存空间,并生成NSObject内存结构的布局。

     int a = 1; int b = 2;

     非对象的基本数据类型都是存放在栈中的。

     所以,我们谈的内存管理,仅仅是针对OC对象而言的。基本数据类型是不需要我们去管理内存的。

     main{

     int b; 栈区

     char s[] = "abc"

     char *p1;

     char *p2 = "123456";  123456\\\\0在常量区,p2在栈上。

     static int c =0 全局(静态)初始化区

     

     w1 = (char *)malloc(10);

     w2 = (char *)malloc(20);

     分配得来得1020字节的区域就在堆区。

     }

     (摘自博客https://www.jianshu.com/p/f3c1b920e8eb

     

     堆区:向高地址扩展的数据结构,是不连续的内存区域。系统是用链表来存储空闲内存地址的。大量的new/delete会造成空间上的不连续,造成大量的碎片。

     

     全局区(静态区) (static) :全局变量和静态变量的存储是放在一起的,初始化的全局变量和静态变量存放在一块区域,未初始化的全局变量和静态变量在相邻的另一块区域,程序结束后有系统释放。
     注意:全局区又可分为未初始化全局区:BSS段和初始化全局区:数据段。

     举例:int a;未初始化的。int a = 10;已初始化的。

     例子代码:

     int a = 10; 全局初始化区

     char *p; 全局未初始化区

     (摘自博客https://www.jianshu.com/p/f3c1b920e8eb

     

     比如static int a;BSS  static int a = 10;数据段

     假如static int a = 0 这种情况呢?初始化了,但是初始化为0

     BSS段在程序执行之前会清0,所以未初始化的全局变量(静态变量)已经是0了。所以这种情况还是存放在BSS段。


     

     关于全局变量与静态全局变量的定义与区别。

     http://blog.youkuaiyun.com/mango_ios/article/details/52686892

     

     

以下数据和变量都存储在那一类内存中?

     int a;int c = 10;

     NSString *name1;NSString *name2 = @"lxx";

     - (void) interviewForStore

     {

     int b;

     NSString *name3 = @"hyc";

     NSString *name4;

     char h[] = "plm";

     static int k = 0;

     

     NSString *m = [[NSString alloc]initWithFormat:@"le"];

     }

     

 另:

野指针:指向垃圾内存(不可用内存)的指针。是非常危险的。

野指针具体是什么?

举例:Animal *ani = [[Animal alloc] init];//这句代码表示,初始化一个Animal对象在堆中,栈中有个指针ani指向该Animal对象。

           [ani setAge:10];//这句话表示给指针ani指向的Animal对象发送一条消息:setAge:。

          [ani release];//这句话表示给指针ani指向的Animal对象发送一条消息:release。即在堆中销毁。

          [ani setAge:7];//给ani指向的Animal对象发消息。但是此时Animal对象已经被销毁了,ani指向的是不可用的内存。继续去访问这块内存。就会报野指针错误。ani就是野指针


僵尸对象:一个已经被释放的对象 就叫做僵尸对象.

内存回收的本质.

1.申请一块空间,实际上是向系统申请一块别人不再使用的空间.

2.释放一块空间,指的是占用的空间不再使用,这个时候系统可以分配给别人去使用.

3.在这个空间分配给别人之前 数据还是存在的.

    3.1.OC对象释放以后,表示OC对象占用的空间可以分配给别人.

    3.2.但是再分配给别人之前 这个空间仍然存在 对象的数据仍然存在.

4.僵尸对象: 一个已经被释放的对象 就叫做僵尸对象.

      当你使用野指针去访问僵尸对象时,有可能会出现问题(该内存空间已经分配给别人),有可能不会出现问题(该内存空间还未分配给别人,对象的数据任然存在)。
有时候我们碰到的(EXE_BAD_ACCESS)一般就是由这个原因引起的。可以开启僵尸模式,定位一下。



思考:以下数据和变量都存储在哪类内存中?

int a;int c = 10;

NSString *name1;NSString *name2 = @"lxx";

- (void) interviewForStore

{

   int b;

   NSString *name3 = @"hyc";

   NSString *name4;

   char h[] = "plm";

   static int k = 0;


   NSString *m = [[NSString alloc]initWithFormat:@"le"];

}








     

<think>嗯,用户想知道程序内存布局中的区、区、静态全局区和代码区的区别和用途。我需要根据提供的引用资料来整理这些信息。首先,区是自动分配释放的,存放局部变量和函数调用信息,对吧?比如引用1和2提到区由编译器管理,存放临时局部变量,先进后出结构。区的话,用户手动分配,比如用malloc,这部分空间大,但需要自己管理,引用2和4都说了区由程序员控制,可能产生内存泄漏。静态全局区包括全局变量和静态变量,生命周期长到程序结束,引用3提到静态存储区在编译期确定大小,地址示例显示不同的变量存放在相邻的位置。代码区存放执行代码,应该是只读的,引用4里的代码区属于程序的一部分。 接下来要比较这四个区域的地址增长方向。区是向低地址增长,区向高地址,静态区可能在中间?代码区是固定的,不可写。另外,各个区的管理方式不同:自动管理,手动,静态区由编译器在程序开始和结束时处理。用途方面,用于函数调用和局部变量,用于动态分配,静态区存全局和静态变量,代码区存指令。 用户可能需要具体的例子来理解,比如局部变量在,动态分配的数组在,全局变量在静态区。还要注意各区的生命周期和访问权限,比如变量函数结束就释放,需要free,静态区一直存在。可能的问题包括内存泄漏、溢出等。现在需要把这些整理成清晰的回答,分点说明每个区的区别和用途,然后生成相关问题。</think>### 程序内存布局详解:区、区、静态全局区、代码区 #### 1. **区(Stack)** - **区别** - **地址方向**:由高地址向低地址生长 - **管理方式**:由编译器自动分配和释放,无需手动干预 - **空间大小**:固定且有限(通常为几MB) - **用途** - 存储**局部变量**(如函数内定义的`int x`) - 保存**函数调用信息**(返回地址、参数、寄存器值等) - 存放**const定义的局部常量**[^2] - **示例** ```c void func() { int a = 10; // 区分配 const int b = 20; // 区分配 } ``` #### 2. **区(Heap)** - **区别** - **地址方向**:由低地址向高地址生长 - **管理方式**:需手动分配(如`malloc`)和释放(如`free`) - **空间大小**:仅受系统内存限制(可动态扩展) - **用途** - 存储**动态分配的数据**(如运行时不确定大小的数组) - 实现**灵活的内存管理**(但需注意内存泄漏问题)[^4] - **示例** ```c int* arr = (int*)malloc(100 * sizeof(int)); // 区分配 free(arr); // 手动释放 ``` #### 3. **静态全局区(Static/Global Area)** - **区别** - **生命周期**:从程序启动到结束 - **管理方式**:编译期确定大小,运行期自动初始化 - **细分区域** - **.data**:已初始化的全局变量和静态变量 - **.bss**:未初始化的全局变量和静态变量(默认置零)[^3] - **用途** - 存储**全局变量**(如`int g_v = 1;`) - 存储**静态局部变量**(如`static int g_vl = 3;`)[^3] - **示例** ```c int global_var = 5; // .data static int static_var; // .bss(默认值为0) void func() { static int count = 0; // 静态局部变量,生命周期与程序一致 } ``` #### 4. **代码区(Text/Code Area)** - **区别** - **权限**:只读(防止程序意外修改指令) - **内容**:编译后的机器指令(二进制代码) - **管理**:由操作系统加载并保护[^4] - **用途** - 存储**程序执行代码**(如函数体、控制逻辑) - 包含**常量字符串**(如`"Hello"`可能存储在此区域) --- ### 对比总结 | **区域** | **管理方式** | **生命周期** | **典型内容** | **地址增长方向** | |------------------|--------------------|--------------------|----------------------------|--------------------| | 区 | 编译器自动管理 | 函数执行期间 | 局部变量、函数参数 | 高地址→低地址 | | 区 | 程序员手动管理 | 直到`free`释放 | 动态分配的数据 | 低地址→高地址 | | 静态全局区 | 编译器静态分配 | 整个程序运行期间 | 全局变量、静态变量 | 固定地址 | | 代码区 | 操作系统加载 | 程序启动到结束 | 可执行指令、常量字符串 | 固定地址 | --- ### 常见问题与风险 1. **溢出**:递归过深或局部变量过大导致空间耗尽(如定义`int arr[10^6]`)[^2] 2. **内存泄漏**:区未释放已分配的内存(如忘记调用`free`)[^4] 3. **指针**:访问已释放的内存(如`free`后仍使用指针---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值