内存5区域 - 堆/栈/全局/常量/代码
一、内存主要五个区域
简单区分如下图:
* 1)栈、堆、全局(静态)区、常量区、代码区的介绍
1、栈 --
由编译器管理,系统自动分配释放。例如:在函数中声明一个局部变量 int b; 系统会自动在栈中开辟空间。
- 存放:函数参数值、局部变量的值(函数中的基本数据类型)、指针 --> 详细课件 OC底层探索18 中内存平移模块。
- 栈区的操作方式类似数据结构中的栈(先进后出)。
在函数体中定义的自动变量通常是在栈上。
2、堆 --
一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。iOS中 ARC会自动管理OC对象的释放。
- 堆的操作方式与数据结构中的堆不同,操作方式类似于链表。
// 注意 p1、p2 本身是在栈区,它俩指向的地址是在堆区 char *p1 = (char *)malloc(10); char *p2 = (char *)malloc(20); // 注意: 存放我们 new 出来的对象,容易产生内存泄漏 memory leak,要做好管理
用 malloc, calloc, realloc 等 分配内存的函数 分配 得到的就是在堆上。
3、全局区(静态存储区)-- 主要是一些未初始化静态变量
编译期管理,系统分配释放。程序退出后自动释放。
- 存放 全局变量 和 静态变量。
全局区又分为全局初始化区和全局未初始化区(文章顶部图)。初始化的全局变量和静态变量存放在全局初始化区,未初始化的全局变量和未初始化的静态变量存放在相邻的另一块区域。
加了 static 修饰符后不管在哪里都存放在全局区(静态存储区);
在所有函数体外定义的是全局变量,存储在全局区(静态存储区),在整个C程序所有源文件中通过extern声明后都可用;
在所有函数体外定义的 static 全局变量存储在全局区(静态存储区),只在该文件中有效,不能extern声明到别的文件用;
在函数体内定义的 static 静态变量存储在全局区(静态存储区),表示只在该函数体内有效,但是其生命周期却变为和整个程序同生同死。
4、常量区 -- 主要是一些已初始化变量和常量
专门放 数字/字符 常量 的地方, 系统管理,程序退出后自动释放 。
5、代码区 --
存放函数的二进制代码。
* 2)C 代码示例:
// main.cpp int a = 0;// 全局初始化区 char *p1;// 全局未初始化区 main() { int b;// 栈 char s[] = "abc";// 栈 char *p2;// 栈 char *p3 = "123456";// 123456\0 在常量区,p3在栈上。 static int c =0;// 全局(静态)初始化区 /* 分配得来得10和20字节的区域就在堆区。 */ p1 = (char *)malloc(10); p2 = (char *)malloc(20); strcpy(p1, "123456");// 123456\0 放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。 }
* 3) 访问一个变量的简单流程
以 person 变量为例,如何对齐进行访问呢?
person 变量的指针是存在栈里面的,通过查询栈 得知变量的地址 p,根据地址 p 可以在堆里面的 p 所对应的空间地址,然后根据堆的地址对 person 的内容进行取值操作。
二、字符串 string 内存优化
注意:关于 string 字符串特殊情况如下,string的内存优化:
字符串有个长度9(字节长度)的限制,当长度不足9时,会被存在栈里面 - NSTaggedPointerString,而当≥9 时string对象存在堆里 - __NSCFString。
// 栈 int i1 = 3; int i2 = i1; NSLog(@"i1=%d =%p i2=%d =%p",i1,&i1,i2,&i2);// i1=3 =0x7ffee230146c i2=3 =0x7ffee2301468 i1 = 4; NSLog(@"i1=%d =%p i2=%d =%p",i1,&i1,i2,&i2);// i1=4 =0x7ffee230146c i2=3 =0x7ffee2301468 i2 = 5; NSLog(@"i1=%d =%p i2=%d =%p",i1,&i1,i2,&i2);// i1=4 =0x7ffee230146c i2=5 =0x7ffee2301468 // 栈 struct MyStruct stru1; stru1.age = 16; struct MyStruct stru2; stru2 = stru1; // stru1=16 =0x7ffee0edc458 stru2=16 =0x7ffee0edc448 NSLog(@"stru1=%d =%p stru2=%d =%p",stru1.age,&stru1,stru2.age,&stru2);// 16 16 stru1.age = 18; // stru1=18 =0x7ffee0edc458 stru2=16 =0x7ffee0edc448 NSLog(@"stru1=%d =%p stru2=%d =%p",stru1.age,&stru1,stru2.age,&stru2);// 18 16 stru2.age = 20; // stru1=18 =0x7ffee0edc458 stru2=20 =0x7ffee0edc448 NSLog(@"stru1=%d =%p stru2=%d =%p",stru1.age,&stru1,stru2.age,&stru2);// 18 20 // 堆 NSMutableArray *arr1 = [@[@(1),@(2)] mutableCopy]; NSMutableArray *arr2 = arr1; NSLog(@"arr1=%@ arr2=%@",arr1[0],arr2[0]);// 1 1 arr1[0] = @(3); NSLog(@"arr1=%@ arr2=%@",arr1[0],arr2[0]);// 3 3 arr2[0] = @(4); NSLog(@"arr1=%@ arr2=%@",arr1[0],arr2[0]);// 4 4 string 的 引用在栈里面,但是其对象在堆 NSString *obj = [NSString stringWithFormat:@"%@",@"12345678"]; NSLog(@"obj=%@ [obj class]=%@ p=%p",obj,[obj class] ,obj);// _objc_decodeTaggedPointer NSString *obj1 = [NSString stringWithFormat:@"%@",@"123456789"]; NSLog(@"obj1=%@ [obj1 class]=%@ p=%p",obj1,[obj1 class],obj1);// NSString *obj2 = [NSString stringWithFormat:@"%@",@"1234567890"]; NSLog(@"obj2=%@ [obj2 class]=%@ p=%p",obj2,[obj2 class],obj2); NSMutableString *s1 = [NSMutableString stringWithFormat:@"%@",@"01234567899"]; NSMutableString *s2 = s1; NSLog(@"s1=%@ %@ %p;s2=%@ %@ %p",s1,[s1 class],s1,s2,[s2 class],s2);// [s1 appendString:@"1"]; NSLog(@"s1=%@ %@ %p;s2=%@ %@ %p",s1,[s1 class],s1,s2,[s2 class],s2);// [s2 appendString:@"2"]; NSLog(@"s1=%@ %@ %p;s2=%@ %@ %p",s1,[s1 class],s1,s2,[s2 class],s2);// NSMutableString *ss1 = [NSMutableString stringWithFormat:@"%@",@"01234567899"]; NSLog(@"ss1=%@ %@ %p",ss1,[ss1 class],ss1);// NSString *str = [NSString new]; NSLog(@"str=%@ [str class]=%@ p=%p",str,[str class],str);// NSString *str1 = @"1234567890"; NSLog(@"str1=%@ [str1 class]=%@ p=%p",str1,[str1 class],str1);//
关于 string 的详细讲解:NSString 的内存管理
待续。。。
Tip:值类型 和 引用类型
值类型和引用类型 https://blog.csdn.net/u011363981/article/details/72526238