iOS 底层探索:内存五大区

iOS 底层探索: 学习大纲 OC篇

前言

  • 这篇开始探索内存管理,这篇主要介绍内存的分布的基础知识概念。

一 、 内存五大区

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

iOS 底层探索:内存五大区_第1张图片
针对4GB运存

栈区(Stack)

  • 定义

    1. 栈是系统数据结构,其对应的进程或者线程是唯一的

    2. 栈是从高地址向低地址扩展扩展的数据结构

    3. 栈是一块连续的内存区域,遵循先进后出(FILO)原则

    4. 栈的地址空间在iOS中是以0x7开头

    5. 栈区一般在运行时分配

  • 存储

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

    1. 局部变量

    2. 函数的参数,例如函数的隐藏参数(id self,SEL _cmd)

  • 优缺点

    1. 优点:因为栈是由编译器自动分配并释放的,不会产生内存碎片,所以快速高效

    2. 缺点:栈的内存大小有限制,数据不灵活

注:

  • iOS主线程栈大小是1MB
  • 其他主线程是512KB
  • MAC只有8M

缓冲区域

  • 定义 : 栈区和堆区中间有小块未使用的内存区域。用于给栈区和堆区之间创建一个缓冲区域

  • 溢出:到达缓冲区的数据向小缓冲区复制的过程中,由于没有注意小缓冲区的边界,导致小缓存区满了,从而覆盖了和小缓存区相邻内存区域的其他数据而引起的内存问题。(就像桶盛水,水多了,自然越界溢出来了。)

堆区(Heap)

  • 定义
  1. 堆是从低地址向高地址扩展。

  2. 堆是不连续的内存区域,类似于链表结构(增删快,查找慢),遵循先进先出(FIFO)原则

  3. 堆的地址空间在iOS中是以0x6开头,其空间的分配总是动态的

  4. 堆区的分配一般是在运行时分配

  • 存储

    1. 堆区是由程序员动态分配和释放的,如果程序员不释放,程序结束后,可能由操作系统回收。
      主要用于存放 :
      a.OC中使用alloc或者 使用new开辟空间创建对象;
      b.C语言中使用malloc、calloc、realloc分配的空间,需要free释放,释放对象在堆区的内存,并将栈中的地址指针置空
  • 优缺点

    1. 优点:灵活方便,数据适应面广泛

    2. 缺点:需手动管理,速度慢、容易产生内存碎片

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

野指针:提前释放了,查询时找不到内容
内存泄露 :没有释放,一直占用内存
过度释放:对已释放的对象进行release操作。

全局静态区(.bss)

  1. 存放全局变量和静态变量
  2. 空间由系统管理。(程序启动时,开辟空间;程序结束时,回收空间;程序执行期间一直存在)在iOS中一般以0x1开头;
  3. static修饰的变量仅执行一次,生命周期为整个程序运行期

常量区(.data)

  1. 存放常量(整型、字符型,浮点,字符串等),整个程序运行期不能被改变。

2 .空间由系统管理,生命周期为整个程序运行期。在iOS中一般以0x1开头。

代码区(即.text)

  • 定义:代码区是编译时分配主要用于存放程序运行时的代码,代码会被编译成二进制存进内存的,
    存放程序执行的CPU指令。(编译期将代码转换为CPU指令)

内核区:

  • 交给系统处理的内核区域, 以4GB手机为例 ,图中高地址 0xc0000000 = 1024 x 1024 x 1024 x 3 总共占3GB,还有1GB就是留给内核区使用的。
  • 0x0040000 ~ 0之间的保留
拓展

defineconst区别:

define: 宏。编译期不会进行语法识别,没有类型。编译期会分配内存。每次使用都会进行宏替换和开辟内存。

const: 常量。编译期会进行语法识别,需要指定类型。编译期不会分配内存,仅在第一次使用时,开辟内存并记录内存地址。后续调用时不会开辟内存,直接返回记录的内存地址。效率更快。内存占用更少。

内存验证

运行下面一段代码,看看变量在内存中是如何分配的

- (void)test {
    
    NSInteger i = 666;
    NSLog(@"NSInteger i -> 内存地址:%p", &i); // 【局部变量】 栈区

    NSString * name = @"QWERDF";
    NSLog(@"NSString name -> 内存地址: %p", name); // 【字符串内容】 存放在常量区
    NSLog(@"NSString name -> 指针地址: %p", &name);// 【局部变量name的指针】 存放在栈区
    
    NSObject * objc = [NSObject new];
    NSLog(@"NSObject objc -> 内存地址: %p", objc);// 【对象的内容】 存放在堆区
    NSLog(@"NSObject objc -> 指针地址: %p", &objc);//【对象的指针】 存放在栈区
}

打印结果: (0x7开头: 栈区 、 0x1开头: 常量区、 0x6开头: 堆区)

2020-11-02 11:19:39.121235+0800 001---- 内存验证[5104:87115] NSInteger i -> 内存地址:0x7ffeeb3e0668 (栈区)
2020-11-02 11:19:39.124740+0800 001---- 内存验证[5104:87115] NSString name -> 内存地址: 0x10481f0e0(常量区)
2020-11-02 11:19:39.128252+0800 001---- 内存验证[5104:87115] NSString name -> 指针地址: 0x7ffeeb3e0660(栈区)
2020-11-02 11:19:39.143804+0800 001---- 内存验证[5104:87115] NSObject objc -> 内存地址: 0x600002fc07d0(堆区)
2020-11-02 11:19:39.145034+0800 001---- 内存验证[5104:87115] NSObject objc -> 指针地址: 0x7ffeeb3e0658(栈区)

你可能感兴趣的:(iOS 底层探索:内存五大区)