Golang

关注公众号 jb51net

关闭
首页 > 脚本专栏 > Golang > Go内存模型

GoLang内存模型详细讲解

作者:上后左爱

go官方介绍go内存模型的时候说:探究在什么条件下,goroutine 在读取一个变量的值的时,能够看到其它 goroutine 对这个变量进行的写的结果,Go内存模型规定了一些条件,在这些条件下,在一个goroutine中读取变量返回的值能够确保是另一个goroutine中对该变量写入的值

栈内存-协程栈-调用栈

为什么go的栈是在堆上?

go 协程栈的位置: go的协程栈位于go的堆内存,go 的gc 也是对堆上内存进行GC, go堆内存位于操作系统虚拟内存上, 记录局部变量,传递参数和返回值 ,go 使用的参数拷贝传递,如果传递的值比较大 注意传递其指针

go 参数传递 使用 值传递, 也就是说传递结构体时候,拷贝结构体的指针,传递结构体指针时候 拷贝的结构体指针 所以对于 只读参数,不进行修改,最好传递结构体指针

协程栈的空间不够大 怎么办?

本地变量太大,栈帧太多

逃逸分析

不是所有的变量都放在协程栈上,栈帧回收后,需要继续使用的变量,或者 太大的变量,分为指针逃逸,空接口逃逸和大变量逃逸,从栈逃逸分配到堆空间上

指针逃逸 (函数返回的指针被其他使用)

func a() *int {
	v :=0
	return &v // 导致 局部变量会分配在堆行 不会分配栈上
}

空接口逃逸(函数的参数是interface{} 函数的实参很可能会逃逸,主要因为interface{} 类型函数往往会使用反射)

fmt.Println(i) // 入参属于interface{} 空接口, 是否有反射查看值是什么类型 逃逸到堆上

大变量逃逸(过大变量导致的空间不足,超过64KB的变量会逃逸)

// 解决协程的栈空间不足

调用栈帧太多(栈扩容)

go 堆内存

操作系统的虚拟内存:操作系统给应用提供的虚拟的内存的空间,背后也是物理内存或者磁盘

go 使用 heapArena每次申请虚拟内存单元 64MB,所有的heapArena 组成 堆内存

线性分配据或者链表分配出现空间碎片,所有go 语言中使用分级分配,避免内存的碎片化,每个内存进行分级思想, mspan n内存管理单元

按照需求进行分级分配,runtime.sizeclass.go 进行分配, 总共有68 个级别。

其中 136 个span , mcentral 属于链接头,其中 68个需要GC扫描,其他68个不需要GC扫描。 mcentral 的属于中心索引,使用互斥锁保护,在高并发的场景下 锁冲突严重,参考GMP模型,增加线程的本地缓存。

堆如何进行分配

go 语言对象的垃圾回收

如何减少GC对性能的分析

如何进行并行GC 提升性能?

难点在于如何进行标记阶段,go 语言采用的 三色标记方法

当三色标记结束后只有黑色的对象,下一次开启恢复成 白色

并发标记的问题(删除)-- 在GC时候 进行对象的指针的变动,针对 并发标记问题 使用 Yuasa 删除屏障, 强制将释放的C指针变成灰色,避免 在GC过程中被粗我䣌标记

Yuasa 删除屏障(s释放的指针进行强制为灰色)

1. 删除屏障可以杜绝在GC标记中删除的问题 ,但是也无法解决并发标记的插入问题

针对插入屏障 使用 Dijkstra 插入屏障 并发标记过程中 将C进行强制置灰,当并发标记过程,新指针指向新的对象,新增的依赖对象 防止错误的GC

混合屏障

被删除的堆对象标记成为灰色

被添加的堆对象标记成为灰色

并发垃圾回收关键在于标记安全,兼顾的安全的效率

GC 优化效率

GC触发的时机

系统定时触发

g0 协程内的sysmon 定时检查 ,在2min 内 forcegcperiod 没有过GC,触发,谨慎调整

用户显示触发

调用runtime.gc 并不推荐

申请内存触发

给申请对象的时候伴随着GC

GC优化原则

尽量少在堆上产生垃圾

内存池化(channel 中 环形池)

减少逃逸 (fmt 包, 返回了指针不是拷贝)

使用空结构体 (不占用空结构体,使用channel 传递空结构体)

使用如下的方式 查看内存

$env:GODEBUG="gctrace=1"

到此这篇关于GoLang内存模型详细讲解的文章就介绍到这了,更多相关Go内存模型内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文