堆和栈区别
- 空间分配区别:
- 栈:由操作系统(编译器)自动分配释放,存放函数的参数值,局部变量的值等,其操作方式类似于数据结构中的栈。
- 堆:一般由代码分配释放,若代码没有显式释放,程序结束时可能由OS回收,分配方式类似链表。
- 缓存方式区别:
- 栈:使用的是一级缓存,通常都是被调用时处于存储空间中,调用完毕立即释放。
- 堆:存放在二级缓存中,生命周期由垃圾回收算法来决定。
数据结构区别:
逃逸分析是一种确定指针动态范围的方法,简单来说就是分析在程序的哪些地方可以访问到该指针。通俗的讲,逃逸分析就是确定一个变量要放堆上还是栈上。规则如下:- 是否在其他地方(非局部)被引用,只要有可能被引用了,那么一定分配到堆上,否则分配到栈上。
- 即使没有被外部引用,但对象过大,无法存放在栈区上,依然有可能分配到堆上。
- 逃逸分析是编译器用于决定变量分配到堆上还是栈上的行为,换句话说,
在编译阶段确定分配到哪里。 栈逃逸就是变量被分配到了堆上。尽可能在栈上分配内存,如果都分配到了堆上,会造成以下后果:go build -gcflags "-m -l" analysis.go,-gcflags表示有参数,参数-m会打印逃逸分析的优化策略,-l会禁用函数内联。go tool compile -S analysis.go,查看汇编代码。垃圾回收
Garbage Collection缩写为GC,是一种自动的存储器管理机制,当一个变量不再被引用时,就应该回收,让出存储空间。
- 简单来说,
垃圾回收(GC)是在后台运行一个守护线程,它的作用是在监控各个对象的状态,识别并且丢弃不再使用的对象来释放和重用资源。 go的垃圾回收使用
三色标记法配合写屏障和辅助GC,三色标记法是标记清除法的增强版本。标记清除法(mark and sweep)
标记:先STW(Stop The World),暂停整个程序的全部运行线程,将被引用的对象打上标记。
- 清除:清除没有被打标记的对象,即回收内存资源,然后恢复运行。
问题在于要通过STW保证GC期间标记对象的状态不能变化,整个程序都要暂停。
三色标记法
1、初始状态所有对象都是白色。
- 2、从root根出发扫描所有根对象(root区域主要是程序运行到当前时刻的栈和全局数据区域?a,b),将他们引用的对象标记为灰色(A,B)。
- 3、灰色对象变为黑色,此外,若灰色对象引用了其他对象,则将被引用的对象标记为灰色(B引用D)
4、遍历灰色对象列表,执行上一步,直到不存在灰色对象,此时白色对象即为垃圾,进行回收。
Go GC如何工作
Golang GC的大部分处理是和用户代码并行的,GC期间用户代码可能会改变某些对象的状态,如何实现并行呢?
- 1、Mark:
- Mark Prepare:初始化GC任务,包括开启写屏障(write barrier)和辅助GC(mutator assist),统计root对象的任务数量等,这个过程需要
STW。 - GC Drains:扫描所有root对象,包括全局指针和goroutine(G)栈上的指针,扫描对于G栈时需停止该G,将其加入标记队列(灰色队列),并循环处理灰色队列的对象,直到灰色对象为空,
该过程后台并行执行。
- Mark Prepare:初始化GC任务,包括开启写屏障(write barrier)和辅助GC(mutator assist),统计root对象的任务数量等,这个过程需要
- 2、Mark Termination:完成标记工作,需要重新扫描全局指针和栈,
因为Mark和用户程序是并行的,所以在该过程中可能会有新的对象分配和指针赋值,这个时候就需要通过写屏障记录下来,重新扫描再检查一下。这个过程也是会STW的。 - 3、Sweep:按照标记结果回收所有的白色对象,
该过程后台并行执行。 4、Sweep Termination:对未扫描的范围进行扫描,只有上一轮GC的清扫工作完成才可以开始新的一轮GC。
写屏障
每一轮GC开始时初始化一个
屏障,记录第一次扫描时各个对象的状态,以便和第二次扫描对比。引用状态变化的对象标记为灰色。前后未变化的再正常处理。辅助GC
Golang实际是把
用户代码-->大段GC--->用户代码分散为用户代码--->小段GC--->用户代码--->小段GC--->用户代码,如果GC速度跟不上分配速度,会把用户逻辑暂停,同时会把用户线程抢占过来加入到垃圾回收里面加快垃圾回收速度。GC触发条件
1、超过内存大小阈值。
- 2、达到定时时间,默认2分钟。
https://blog.csdn.net/weixin_40108561/article/details/104735390
