洋仔的博客 洋仔的博客
首页
  • 个人心法总结

    • 价值心法
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • iOS基础知识
  • 前端
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 投资体系
  • 毛选
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

洋仔

奋斗的小青年
首页
  • 个人心法总结

    • 价值心法
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • iOS基础知识
  • 前端
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 投资体系
  • 毛选
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 技术文档

  • GitHub技巧

  • Nodejs

  • 博客搭建

  • iOS基础知识

    • iOS底层相关

    • Runloop系列

    • Runtime系列

    • 内存管理系列

    • Block系列

    • 线程系列

    • KVC跟KVO系列以及通知中心

    • UI系列

    • 离屏渲染系列

    • 组件化系列跟架构

    • OC跟webview交互系列

    • 持久化系列

    • APP编译系列

    • APP性能优化系列

    • cocoapods系列

    • swift系列

    • Git系列

    • 网络相关

    • 三方库系列

    • 系统原理

      • 程序执行的流程
      • 五大内存区
        • 五大内存区
          • 栈区
          • 堆区
        • 全局区
        • 总结
    • 总结系列

    • 算法系列

    • 数据结构系列

  • 前端

  • 技术
  • iOS基础知识
  • 系统原理
洋仔
2024-07-20
目录

五大内存区

# 五大内存区

在iOS中,内存主要分为栈区、堆区、全局区、常量区、代码区五大区域

# 栈区

定义

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

栈是 向低地址扩展 的数据结构

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

栈的地址空间在iOS中是以 0X7或者0X16开头

栈区一般在 运行时分配

存储

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

局部变量

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

优缺

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

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

OS X(MAC上)主线程栈大小是8MB

iOS主线程栈大小是1MB

其他线程是512KB

# 堆区

堆是 向高地址扩展 的数据结构

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

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

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

储存

堆区是由程序员动态分配和释放的,如果程序员不释放,程序结束后,可能由操作系统回收,主要用于存放

OC中使用alloc或者 使用new开辟空间创建对象 C语言中使用malloc、calloc、realloc分配的空间,需要free释放

优缺

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

缺点:需手动管理,速度慢、容易产生内存碎片 当需要访问堆中内存时,一般需要先 通过对象读取到栈区的指针地址 ,然后通过 指针地址 访问堆区

# 全局区

全局区是编译时分配的内存空间

在iOS中一般以 0x1 开头

在程序运行过程中,此内存中的数据一直存在,程序结束后由系统释放,主要存放

  • 未初始化的全局变量和静态变量,即BSS区(.bss)
  • 已初始化的全局变量和静态变量,即数据区(.data) BBS:Block Started by Symbol

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

常量区 常量区(.rodata)是 编译时分配 的内存空间,在程序结束后由系统释放,主要存放

已经使用了的,且没有指向的字符串常量 字符串常量因为可能在程序中被多次使用,所以在程序运行之前就会提前分配内存

代码区

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

# 总结

一个程序在内存上由BSS段、data段、text段三个组成的。在没有调入内存前,可执行程序分为代码段、数据区和未初始化数据区三部分

BSS段:(Block Started by Symbol) 通常是指用来存放程序中未初始化的全局变量的一块内存区域,属于静态内存分配。BSS段的内容并不存放在磁盘上的程序文件中。原因是内核在程序开始运行前将它们设置为0,需要存放在程序文件中的只有正文段和初始化数据段。text段和data段在编译时已经分配了空间,而BSS段并不占用可执行文件的大小,它是由链接器来获取内存的。

数据段:(data segment)

通常是指用来存放程序中已初始化的全局变量的一块内存区域,属于静态内存分配。总结为:初始化的全局变量和静态变量在已初始化区域,未初始化的全局变量和静态变量在BSS区。

代码段:(code segment/text segment)

通常是指用来存放程序执行代码的一块内存区域。该区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。

堆(heap):

用于动态分配内存,位于BSS和栈中间的地址区域,由程序员申请分配和释放。堆是从低地址位向高地址位增长,采用链式存储结构。频繁的malloc/free造成内存空间的不连续,会产生碎片。当申请堆空间时库函数是按照一定的算法搜索可用的足够大的空间,因此堆的效率比栈要低的多。注:与数据结构中的堆不是一个概念,但堆的分配方式类似于链表。

栈(stack):

由编译器自动释放,存放函数的参数值、局部变量等。每当一个函数被调用时,该函数的返回类型和一些调用的信息被存放到栈中,这个被调用的函数再为它的自动变量和临时变量在栈上分配空间。每调用一个函数一个新的栈就会被使用。栈区是从高地址位向低地址位增长的,是一块连续的内存区域,最大容量是由系统预先定义好的,申请的栈空间超过这个界限时会提示溢出。

堆和栈的区别在于

1)管理方式:栈由编译器自动管理,无需人为控制。而堆释放工作由程序员控制,容易产生内存泄漏(memory leak)。

2)空间大小:在32位系统下,堆内存可以达到4G的空间(虚拟内存的大小,有面试官问过),从这个角度来看堆内存大小可以很大。但对于栈来说,一般都是有一定的空间大小的

3)碎片问题:堆频繁new/delete会造成内存空间的不连续,造成大量的碎片,使程序效率降低(重点是如何解决?如内存池、伙伴系统等)。对栈来说不会存在这个问题,因为栈是先进后出,不可能有一个内存块从栈中间弹出。在该块弹出之前,在它上面的(后进的栈内容)已经被弹出。

4)生长方向:堆生长(扩展)方向是向上的,也就是向着内存地址增加的方向;栈生长(扩展)方向是向下的,是向着内存地址减小的方向增长, 可以看第一张图。

5)分配方式:堆都是动态分配的,没有静态分配的堆。而栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,如局部变量分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,它的动态分配是由编译器进行释放,无需我们手工实现。

6)效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持(有专门的寄存器存放栈的地址,压栈出栈都有专门的机器指令执行),这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的(可以了解侯捷老师的内存管理的视频,关于malloc/realloc/free函数等)。例如分配一块内存,堆会按照一定的算法,在堆内存中搜索可用的足够大小的空间,如果没有(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。总之,堆的效率比栈要低得多。

参考文档:https://blog.csdn.net/m0_74703932/article/details/139127718

编辑 (opens new window)
上次更新: 2024/10/23, 23:26:17
程序执行的流程
总结面试题有感

← 程序执行的流程 总结面试题有感→

最近更新
01
数组
10-25
02
数组双指针系列之对撞指针
10-25
03
数组双指针系列之快慢指针
10-25
更多文章>
Theme by Vdoing | Copyright © 2019-2024 Evan Xu | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式