Skip to main content

One post tagged with "内存结构"

View All Tags

内存(Memory)

  • 只读数据段(RODATA/RDATE)
  • 堆(Heap)
  • 栈(Stack)
  1. 只读数据段

    • 存放常量
  2. Stack

    • 是程序运行的基础
    • 每当一个函数被调用时,一块连续的内存就会在栈顶被分配出来,这块内存被称为帧(frame).
    • 自顶向下增长,出去入口帧(entry frame),就是main()对应的帧,随着main()函数一层层调用,栈会一层层扩展,调用结束,栈又会一层层回溯,把内存释放
    • 调用过程中,一个新的帧会分配足够的空间存储寄存器的上下文
    • 在函数里使用到的通用寄存器在栈保存一个副本,函数调用结束,通过副本,可以恢复出原本的寄存器的上下文
    • 函数的局部变量也都会在帧分配的时候被预留出来
  3. Heap

    • 当我们需要动态大小的内存时,只能用堆
    • 动态生命周期的内存也需要分配到堆上
    • 堆上分配出来的每一块内存需要显式地释放,这就使内存有更加灵活的生命周期

数据什么时候放在栈上什么时候放在堆上?

  • 在编译时,一切无法确定大小或者可以改变的数据,都无法安全地放在栈上,最好放在堆上
  • 只要可能,我们应该把变量分配到栈上,这样可以达到更好的运行速度
  • 栈上存放的数据是静态的,固定大小,固定生命周期,在编译期可以确定
  • 堆上存放的数据是动态的,不固定大小,不固定生命周期,在编译期不能确定

数据

  • 类型
  • 指针
  • 引用
  • 类型是对值的区分,包含了值在内存中的长度,对齐以及值可以进行操作等信息
  • 一个值是符合一个特定类型的数据的某个实体
  • 值是无法脱离具体的类型讨论的
  1. 类型
  • 原生类型(primitive type): 固定大小,可以分配在栈上
    • 字符
    • 整数
    • 浮点数
    • 布尔值
    • 数组
    • 元组
    • 枚举
    • 指针: 指针的使用限制少,如果没有正确的类型解引用一个指针,会引发各种内存问题,造成潜在安全漏洞
    • 引用: 引用的解引用访问是受限的,它只能解引用到它引用数据的类型,不能用作它用
  • 组合类型(composite type)
    • 结构体: 多个类型组合在一起共同表达一个值的复杂数据结构
    • 标签联合: 可以存储一组不同但固定的类型的类型中的某个类型的对象,具体是由标签决定
  • 其他类型
    • 函数
    • 闭包

代码

  • 函数: 对代码中重复行为的抽象
  • 方法: 在类中或者对象中定义的函数,只为某种类型定义的函数,和对象的指针发生关联
  • 闭包: 将函数或者说代码和其环境(自由变量)一起存储的一种数据结构。
  • 接口: 将使用方和实现方隔离开来,使两者不直接有依赖关系,大大提高了复用性和扩展性
  • 虚表: 存储多态接口方法的列表,辅助运行时代码执行,方便进行动态分派,是运行时多态的基础

面向接口的设计师软件开发中的重要能力,当我们在运行期使用接口来引用具体类型的时候,代码就具备了运行时多态的能力。 在运行时,一旦使用了关于接口的引用,变量原本的类型被抹去,我们无法单纯从一个指针分析出这个引用具备什么样的能力。 在生成这个引用的时候,我们需要构建胖指针,除了指向数据本身,还需要指向一张涵盖这个接口所支持方法的列表。 这个列表就是我们熟知的虚表(virtual table)

运行方式

  • 并发: 是一种能力, 同时与多件事情打交道的能力(交替执行多件不同的事情)
  • 并行: 是一种手段, 在多个CPU Core上执行同样的代码
  • 同步: 一个任务开始后,后续的操作会阻塞,直到这个任务结束,保证了代码的因果关系,保证程序正确性
  • 异步: 指一个任务开始执行后,与它没有因果关系的其它任务可以正常执行,不必等待前一个任务结束
  • Promise / Future / Delay / Deferred: 是一个对象,用来描述在未来某个时刻才能获得的结果的值
  • async / await: async用来定义一个可以并发执行的任务, await触发这个任务并执行

在遭遇I/O处理时,高效CPU指令低效I/O之间的巨大鸿沟,成为了软件的性能杀手。

和内存访问相比,I/O操作的访问速度低了两个数量级,一旦遇到I/O操作CPU就只能闲置来等待I/O设备运行完毕。

因此操作系统为应用程序提供了异步I/O,让应用可以在当前I/O处理完毕之前,将CPU时间用作其它任务的处理

很多拥有高并发处理能力的编程语言,会在用户程序中嵌入一个M:N的调度器,把M个并发任务,合理地分配在NCPU Core上并行运行, 让程序的吞吐量达到最大。比如Golang的Goroutine

Promise一般存在三个状态:

  • 初始状态, Promise还未运行
  • 等待(pending)状态, Promise已运行,还未结束
  • 结束状态, Promise成功解析出一个值,或者执行失败

编程范式

  • 泛型编程

    • 数据结构的泛型: 把参数化数据结构理解成一个产生类型的函数,是一种高级抽象
    • 代码的泛型化: 当我们使用泛型结构编写代码时,相关的代码也需要额外的抽象

    通过参数化让数据结构像函数一样延迟绑定,提升通用性,类型的参数可以用接口约束,使类型满足一定的行为, 同时,在使用泛型结构时,我们的代码也需要更高的抽象度。

鱼雪