美文网首页
iOS 内存五大区

iOS 内存五大区

作者: NJKNJK | 来源:发表于2021-03-05 15:12 被阅读0次

    1.介绍

    iOS中,内存主要分为栈区堆区全局区常量区代码区五大区域,如下图所示:

    五大内存区.png

    2. 栈区

    2.1 介绍

    • 栈是系统的数据结构,其对应的进程或者线程是唯一的
    • 栈是从高地址向低地址扩展的数据结构
    • 栈是一块连续的内存区域,遵循FILO,先进后出原则
    • 在iOS中一般以0x7开头,在运行时分配
    • 编译器自动分配释放,不会产生内存碎片,快速高效
    • 内存大小有限制,数据不灵活(主线程栈是1MB,其他线程是512KB

    2.2 存储的数据

    栈区的内存是由编译器自动分配并释放的,主要用来存储:

    • 函数内部定义的局部变量和数组
    • 函数的参数
    • 栈区的内存空间是由系统管理,在调用的时候开辟空间,函数调用完成,就收回空间。

    3. 堆区

    3.1 介绍

    • 和栈相反,堆是低地址向高地址扩展的数据结构
    • 堆是不连续的内存区域,类似于链表,遵循先进先出原则
    • 堆的地址空间在iOS中以0x6开头,一般在运行时动态分配空间
    • 需要手动管理,容易产生内存碎片

    3.2 存储的数据

    • 堆区主要由开发者动态分配和释放,如果开发者不释放,程序结束后,则由操作系统回收

    • OC中使用alloc或者使用new开辟空间创建对象

    • C语言中使用malloccallocrealloc分配的空间,需要free释放

    注意:当需要访问堆中内存时,一般需要先通过对象读取到栈区的指针地址,然后通过指针地址访问堆区

    4. 全局区 (静态区,.bss & .data)

    • 全局区是在编译时分配的内存空间
    • 全局区的地址空间在iOS中一般以0x1开头
    • 程序运行中,内存中的数据一致存在,程序结束后由系统释放
    • .bss(BSS)区存放未初始化的全局变量和静态变量
    • .data 数据区存放已经初始化的全局变量和静态变量

    其中,全局变量是指变量值可以在运行时被动态修改,而静态变量是static修饰的变量,包含静态局部变量和静态全局变量。

    5. 常量区 (.rodata)

    常量区是编译时分配的内存空间,在程序结束后由系统释放,主要存放已经使用了的,且没有指向的字符串常量。
    字符串常量可能会在程序中多次使用,所以在程序运行之间就会提前分配内存。

    6. 代码区(.text)

    代码区是编译时分配主要用于存放程序运行时的代码,代码会被编译成二进制存进内存的。

    1. 验证

    举个例子,看看下面的代码,变量在内存中是如何分配的:

    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        NSInteger i = 123;
        NSLog(@"i的内存地址:%p", &i);
        
        NSString *string = @"CJL";
        NSLog(@"string的内存地址:%p", string);
        NSLog(@"&string的内存地址:%p", &string);
        
        NSObject *obj = [[NSObject alloc] init];
        NSLog(@"obj的内存地址:%p", obj);
        NSLog(@"&obj的内存地址:%p", &obj);
    }
    

    输出结果如下:

    2021-01-19 20:38:48.645299+0800 KVC[42501:2211601] i的内存地址:0x7ffee4107138
    2021-01-19 20:38:48.645439+0800 KVC[42501:2211601] string的内存地址:0x10baf9040
    2021-01-19 20:38:48.645553+0800 KVC[42501:2211601] &string的内存地址:0x7ffee4107130
    2021-01-19 20:38:48.645663+0800 KVC[42501:2211601] obj的内存地址:0x600001ba8110
    2021-01-19 20:38:48.645761+0800 KVC[42501:2211601] &obj的内存地址:0x7ffee4107128
    
    • 对于局部变量i,地址是0x7开头的,存放在栈区
    • 对于字符串对象stringstring的对象地址是0x10baf9040,存放在常量区,存放string对象指针的地址是0x7ffee4107130,存放在栈区
    • 对于alloc创建的对象obj,对象的内存地址是0x600001ba8110,存放在堆区,存放obj对象指针的地址是0x7ffee4107128,存放在栈区

    8. 函数栈&栈帧

    • 函数栈又称为栈区,在内存中从高地址往低地址分配,与堆区相对
    • 栈帧是函数(运行中)占用的一块独立的连续内存区域
    • 应用中新创建的每个线程都有专用的栈空间,栈可以在线程期间自由使用。而线程中有千千万万的函数调用,这个函数共享一个栈空间。栈帧就是这个函数在栈空间里独占的部分,所有的栈帧就组成了这个线程完整的栈。
    • 函数调用是发生在栈上的,每个函数的相关信息(例如局部变量调用记录等)都存储在一个栈帧中,每执行一个函数调用,就会生成一个与其相关的栈帧,然后将其栈帧压入函数栈,当函数执行完毕,对应的栈帧也会出栈并释放掉。

    如下图所示,是经典图 - ARM的栈帧布局方式:

    内存详情.png
    • 其中main stack frame为main函数的栈帧,func1 stack frame为当前函数(被调用者)的栈帧
    • 栈底是高地址,向下增长
    • FP是栈基址,指向栈帧起始地址,SP是栈顶指针,指向栈顶位置
    • ARM压栈是有顺序的,依次为当前函数指针PC、返回指针LR、栈指针SP、栈基址FP、传入参数个数及指针、本地变量和临时变量。如果函数准备调用另一个函数,跳转之前临时变量区先要保存另一个函数的参数
    • ARM也可以用栈基址和栈指针明确标示栈帧的位置,栈顶指针SP一直移动,ARM的特点是,两个栈空间内的地址(SP+FP)前面,必然有两个代码地址(PC+LR)明确标示着调用函数位置内的某个地址。

    注意:一般情况下是不需要考虑堆栈的带下,但是事实上它们都是有限制的,过多的递归会导致栈溢出,过多的alloc对象会导致堆溢出。

    事实上,堆栈中存放的都为指针,对象本身也是指针的合集,指针最终都指向常量区、全局区、代码区

    相关文章

      网友评论

          本文标题:iOS 内存五大区

          本文链接:https://www.haomeiwen.com/subject/pewrqltx.html