linux 进程内存原理
文章目录
linux 进程 内存原理
|
|
虚拟内存 和 物理内存 会有映射关系
linux 使用的是多级索引页表
Linux采用了一种同时适用于32bit和64bit系统的分页模型。32bit系统一般采用两级页表就足够了,但64bit系统需要更多的分页。Linux 2.6.10版本采用三级分页,从2.6.11版本开始采用了四级分页。
CPU 对内存的 访问
- CPU 有个 Memory Management Unit (MMu)单元
- cpu 把虚拟地址 给 MMU ,MMU 去物理内存 查询页表,得到实际的物理地址
- CPU 维护一份缓存 Translation Lookaside buffer (TLB), 缓存虚拟地址和物理地址 的映射关系
goroutine
go语言 基于 GMP 模型 实现 用户态线程
- Goroutine: 表示协程,每个 goroutine 都有自己的栈空间,定时器,初始化栈空间为 2k,空间会随着需求增加
- Machine: 抽象化代表内核线程,记录内核线程栈信息,当 goroutine 调度到线程时, 使用该goroutine 自己的栈信息
- Process , 代表调度器,负责 goroutine,维护一个 本地 goroutine 队列,M从P上获得Goroutine 并执行,同时还负责部分内存管理
GMP 模型细节
以一定的几率,比如说 1/6 去全局队列中获得任务执行,如果 全局队列没有任务,局部空闲队列也没有任务,就采用工作窃取的机制 去其他的队列中获取任务来执行
G状态 G的主要几种状态:
本文基于Go1.13,具体代码见(/src/runtime/runtime2.go)
_Gidle:刚刚被分配并且还没有被初始化,值为0,为创建goroutine后的默认值
_Grunnable: 没有执行代码,没有栈的所有权,存储在运行队列中,可能在某个P的本地队列或全局队列中(如上图)。
_Grunning: 正在执行代码的goroutine,拥有栈的所有权(如上图)。
_Gsyscall:正在执行系统调用,拥有栈的所有权,与P脱离,但是与某个M绑定,会在调用结束后被分配到运行队列(如上图)。
_Gwaiting:被阻塞的goroutine,阻塞在某个channel的发送或者接收队列(如上图)。
_Gdead: 当前goroutine未被使用,没有执行代码,可能有分配的栈,分布在空闲列表gFree,可能是一个刚刚初始化的goroutine,也可能是执行了goexit退出的goroutine(如上图)。
_Gcopystac:栈正在被拷贝,没有执行代码,不在运行队列上,执行权在
_Gscan : GC 正在扫描栈空间,没有执行代码,可以与其他状态同时存在
P的状态 _Pidle :处理器没有运行用户代码或者调度器,被空闲队列或者改变其状态的结构持有,运行队列为空
_Prunning :被线程 M 持有,并且正在执行用户代码或者调度器(如上图)
_Psyscall:没有执行用户代码,当前线程陷入系统调用(如上图)
_Pgcstop :被线程 M 持有,当前处理器由于垃圾回收被停止
_Pdead :当前处理器已经不被使用
M的状态 自旋线程:处于运行状态但是没有可执行goroutine的线程(如下图),数量最多为GOMAXPROC,若是数量大于GOMAXPROC就会进入休眠。
非自旋线程:处于运行状态有可执行goroutine的线程。
golang 内存回收原理
-
引用计数 (python,PHP,Swift)
-
标记-清除(golang)
- 从根变量遍历所有应用到对象,没有被标记的对象进行回收
- 缺点:需要 stop the world ,暂停程序运行
- golang 具体使用的是 三色标记算法
-
分代收集 (java)
go语言垃圾回收触发机制
-
内存分配量达到阈值触发 GC
- 每次内存分配时候会检查当前内存分配量是否已达到阈值,达到阈值立即启动GC
- 定期触发GC
- 默认情况下,最长 2分钟触发一次 GC, 这个间隔在
src/runtime/proc.go:forcegcperiod
变量中声明
- 默认情况下,最长 2分钟触发一次 GC, 这个间隔在
- 手动触发
- 调用
runtime.GC()
来手动触发GC 。这个主要用于GC 性能测试和统计
- 调用
golang GC 工作流程
- Mark:
- 先 mark prepare: 初始化 GC 任务,包括开启写屏障(write barrier) 和 辅助 GC (Mutator assist ),统计 GC root 对象的任务数量,这个过程需要短暂的 STW
- GC drains: 扫描 所有 root 对象, 包括全局 指针, goroutine(G) 栈上的指针,将其加入标记队列(灰色队列),并循环处理灰色队列的对象,知道灰色队列为空,这个过程后台并行执行
- Mark termination: 完成标记,要重新 扫描 全局指针,因为 mark 和 用户程序是并行的,所以这个过程中 可能会有新的对象分配和制作赋值,需要通过写屏障(write barrier) 记录下来, re-scan 再检查一下会 STW
- Sweep: 标记结果 将 白色对象 回收,这个过程是并行执行的
- Sweep termination , 未清扫的span 进行清扫,只有上一轮 GC 的清扫工作完成后才能开始新一轮的 GC
调优命令
|
|
文章作者 LYR
上次更新 2021-08-14