iOS中内存分配

 

Posted by     DH on August 11, 2017

概述

在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”