概述
在iOS的内存中,主要包括如下的几个部分:代码区、常量区、全局静态区(已初始化全局静态数据和未初始化全局静态区)、堆区、栈区。
他们在内存中的大致分布如下:
各分区存储的数据类型
代码区
代码区顾名思义就是存储代码的,这个区域主要存储的是代码的二进制文件,为了防止程序被篡改,这一部分只允许读不允许写。而这个区域中包括了操作代码和备操作的 对象(对象的地址)。如果是立即数,直接包含在代码中,如果是局部变量,先在栈区分配空间,然后引用这个地址。如果是未初始化全局静态区,也是引用地址。
常量区
顾名思义就是存储常量的内存区域。
举例:
-(void)test{ . . . . NSString *str= @“123456”; . . . }
str是局部变量,在栈上分配内存,123456是常量,在常量区分配内存。
静态全局区
- 全局静态已初始化区
该区域包含了在程序中明确被初始化的全局变量、静态变量(包括静态全局变量和静态局部变量)。
- 全局静态未初始化区
包含了程序中未初始化全局变量。
堆区
是由程序员来申请和释放的内存区域(ARC环境下,不需要程序员管理),用于动态内存分配,如果程序员不释放,程序结束时,可能会由操作系统回收 , 比如在ios 中 alloc 在堆中分分配一块内存。
栈区
由编译器进行分配和释放。主要用于存储局部变量的值、参数的值等。通常指的就是{}中定义的局部变量,但是不包括static修饰的变量。同时,栈区还用于存储函数的 返回地址和参数等,这在函数调用的时候有用。
iOS内存分配方式
主要包含动态分配和静态分配两种
静态分配
是在程序编译的期间就分配好内存。
动态分配
程序执行期间,通过alloc函数进行申请。
两种内存分配方式不同
静态内存分配的变量是有名字的,可以通过名字访问,动态对象没有名字,需要用指针进行访问。
静态对象由编译器进行分配和释放,动态对象,由程序员进行分配释放。
堆和栈的区别
(1)申请和释放方式不同
堆是通过程序员,显示的掉用库函数进行申请,使用完毕后,调用函数进行释放。栈是编译器自行分配和释放内存。
(2)申请过程,申请后系统响应不同
栈的分配,每次函数调用都会编译器都会去申请一块栈控件,调用结束后,释放这个内存。如果申请的内存超过了栈剩余的内存,就会报出内存溢出的错误,否则就进行分配。
关于堆的分配,在操系统中,有一个链表存储着空闲的内存,在操作系统收到堆申请是,就会遍历这个链表,当找到第一个大于申请的内存时,就进行内存分配,将这个堆 节点从空闲链表中删除。当堆释放时,再将这个节点加入到控件链表中。
(3)申请大小不同
栈是一段连续的区域,在程序运行之初就已经确定,假如要申请的空间大于栈的剩余空间,就会出现内存溢出。我从别人的博客了解到好像是2M,具体的没有查到,所以栈不够 灵活,大小较小。
堆的大小取决于操作系统的虚拟内存大小。在操作系统中,有一个记录空闲内存的链表,所以,堆是不连续的,分配很灵活。
(4)分配效率
栈是系统提供的数据结构,并且由系统进行管理,计算机底层进行了支持,有专门的寄存器和操作系统指令进行处理,效率高;
堆由程序员管理,需要显示调用库函数进行操作,库函数会按照一定算法进行搜索满足要求的内存,如果没有满足要求的内存,还会调用其他的方法去处理碎片化问题,所以, 效率较低。
(5)内存方向
堆:从低地址到高地址
栈:从高地址到低地址
(6)分配方式的不同
堆:动态分配,没有静态分配
栈:动态分配和静态分配。静态分配是由编译器完成。动态分配由alloc函数进行,但是不是由程序员显示的调用alloc函数,而是编译器调用
例子
补充static、const相关知识
1、当static关键字修饰局部变量时,该局部变量只会初始化一次,在系统中只有一份内存
2、static关键字不可以改变局部变量的作用域,但是可延长局部变量的生命周期,该变量直到整个项目结束的时候才会被销毁
3、static修饰的全局变量:作用域仅限于当前文件,外部类不可以访问到该变量
4、extern:引用关键字,当某一个全局变量,没有用static修饰时,其作用域为整个项目文件,若是在其他类想引用该变量,则用extern关键字,例如,想引用其他类的全局变量,int age = 10;则在当前类中实现,extern int age;也可以在外部修改该变量,extern int age = 40;,若某个文件中的全局变量不想被外界修改,则用static修饰该变量,则其作用域只限于该文件。
静态变量:iOS的静态变量使用static修饰,该变量只会被初始化一次,并且作用于仅在该文件中,C或者Java中static关键字修饰的变量,可以使用类名直接拿到这个变量对象,在其他类中可以进行修改。但是在OC中static修饰的变量是不能通过类名直接访问的,它只作用于它声明所在的.m文件中。
静态常量:const修饰的变量是不可变的,注意const必须放在“*”后面,否则内存中的内容依旧可以改变。
全局变量:extern NSString * str = @”DH”; 添加了extern在其他文件中也可以访问,加上const就只能读,不能修改了extern NSString * const str = @”DH”