内存五大区 & 虚拟内存

1,416 阅读6分钟

标签: 内存五大区 虚拟内存


本章节 只要介绍 内存五大区 & 虚拟内存

  1. 栈区
  2. 堆区
  3. 全局静态区
  4. 常量区
  5. 代码区
  6. 虚拟内存&物理内存

1 内存的五大区

按照地址从高到低排列: 栈区 -> 堆区 -> 全局静态区 -> 常量区 -> 代码区 (内核区和保留部分不再考虑范围内) ![此处输入图片的描述][1] 拓展:

  1. 内存五大区,实际是指虚拟内存,而不是真实物理内存。
  2. iOS系统中,应用的虚拟内存默认分配4G大小,但五大区只占3G,还有1G是五大区之外的内核区

1.1 栈区

  • 函数内部定义的局部变量和数组,都存放在栈区; (比如每个函数都有的(id self, SEL _cmd))
  • 栈区的内存空间由系统管理。(函数调用时开辟空间,函数调用结束时回收空间)
  • 栈是从高地址向低地址扩展,是一块连续的内存区域,遵循FILO先进后出原则,效率高。
  • 栈区一般在运行时进行分配

1.2 堆区

  • 空间最大,由我们手动管理。(ARC自动管理)
  • 堆是从低地址向高地址扩展。
  • malloc、calloc、realloc开辟:
  • 堆区开辟空间,可以是不连续的内存区域,以链表结构存在(增删快,查找慢)。返回首地址存放在栈区。
  • free回收。释放对象在堆区的内存,并将栈中的地址指针置空。

1.3 全局静态区(.bss)

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

1.4 常量区(.data)

  • 存放常量(整型、字符型,浮点,字符串等),整个程序运行期不能被改变。
  • 空间由系统管理,生命周期为整个程序运行期。

1.5 代码区(.text)

存放程序执行的CPU指令。(编译期将代码转换为CPU指令)

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

    NSString * name = @"ypy";
    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);//【对象的指针】 存放在栈区
2021-08-06 14:23:42.296970+0800 001---memory[63570:4007723] NSInteger i -> 内存地址:0x7ffee721ebd8
2021-08-06 14:23:42.297069+0800 001---memory[63570:4007723] NSString name -> 内存地址: 0x1089e1220
2021-08-06 14:23:42.297153+0800 001---memory[63570:4007723] NSString name -> 指针地址: 0x7ffee721ebd0
2021-08-06 14:23:42.297236+0800 001---memory[63570:4007723] NSObject objc -> 内存地址: 0x600001065000
2021-08-06 14:23:42.297328+0800 001---memory[63570:4007723] NSObject objc -> 指针地址: 0x7ffee721ebc8

(0x7开头: 栈区 、 0x1开头: 常量区、 0x6开头: 堆区)

  • 对于局部变量i,从地址可以看出是0x7开头,所以i存放在栈区
  • 对于字符串对象name,分别打印了name的对象地址 和 name对象的指针地址
    • name的对象地址是以0x1开头,说明是存放在常量区
    • name对象的指针地址是以0x7开头,说明是存放在栈区
  • 对于alloc创建的对象obj,分别打印了obj的对象地址 和 objc对象的指针地址
    • objc的对象地址是以0x6开头,说明是存放在堆区
    • objc对象的指针地址是以0x7开头,说明是存放在栈区

2 虚拟内存 & 物理内存

早期计算机,没有虚拟内存概念,只有物理内存,每个应用都直接全部信息写入内存条中的。当内存条的空间不够时(被其他应用占据了),就会报内存警告。这时我们只能手动关闭一些应用,腾出点内存来让当前应用运行。

2.1 物理内存

物理内存: 内存条的真实大小。 (4G内存条,物理内存就是4G)

2.1.1 物理内存的缺陷

  • 内存不够: 每个应用一打开,就把所有信息都加载进去,占用太多资源。大软件直接无法加载。
  • 不安全: 每次加载应用,内存地址就固定了,很容易被人直接通过内存地址去篡改数据。 早期本地外挂,就是通过内存地址去篡改数据。

2.2 虚拟内存

后来,经过研究,发现每个应用在内存中使用的部分,仅占该应用的小部分(活跃部分)。于是将内存均匀分割成很多页。应用也不用一启动就全部加载进去,而是每个启动的应用,都分配一个虚拟的内存大小,里面也跟物理内存一样切割成一样大小的的内存页。

2.3 拓展

  • 内存管理单元
    • MMU:(Memory Management Unit) 内存管理单元,有时称作PMMU(paged memory management unit)分页内存管理单元
    • 负责处理中央处理器(CPU)的内存访问请求的计算机硬件。
  • 内存页大小
    • Linux和MacOS系统:每页4K
    • iOS系统: 每页16K
  • 页表
    • 应用的虚拟内存与物理内存的地址映射关系表
  • 虚拟空间大小
    • 每个应用(进程)默认可以分配4G大小。但它实际只是一张页表,记录映射关系就可以。 页表存放在操作系统的内存区域。
    • 应用用到的,都是物理内存,实际占有物理内存大小是应用运行时决定的。
  • 野指针:提前释放了,查询时找不到内容
  • 内存泄露 :没有释放,一直占用内存
  • 过度释放:对已释放的对象进行release操作。
  • 缓冲区域
    • 栈区和堆区中间有小块未使用的内存区域。用于给栈区和堆区之间创建一个缓冲区域
  • 溢出:
    • 到达缓冲区的数据向小缓冲区复制的过程中,由于没有注意小缓冲区的边界,导致小缓存区满了,从而覆盖了和小缓存区相邻内存区域的其他数据而引起的内存问题。
  • define和const区别:
    • define: 宏。编译期不会进行语法识别,没有类型。编译期会分配内存。每次使用都会进行宏替换和开辟内存。
    • const: 常量。编译期会进行语法识别,需要指定类型。编译期不会分配内存,仅在第一次使用时,开辟内存并记录内存地址。后续调用时不会开辟内存,直接返回记录的内存地址。效率更快。内存占用更少。 [1]: yupuyang.gitlab.io/images/dash…