• Go核心编程(四) -- 内存模型


    栈内存(协程栈,调用栈)
    1. 为什么go的栈是在堆上?
    • go 协程栈的位置: go的协程栈位于go的堆内存,go 的gc 也是对堆上内存进行GC, go堆内存位于操作系统虚拟内存上, 记录局部变量,传递参数和返回值 ,go 使用的参数拷贝传递,如果传递的值比较大 注意传递其指针
      在这里插入图片描述
      go 参数传递 使用 值传递, 也就是说传递结构体时候,拷贝结构体的指针,传递结构体指针时候 拷贝的结构体指针 所以对于 只读参数,不进行修改,最好传递结构体指针
    1. 协程栈的空间不够大 怎么办?
    • 本地变量太大,栈帧太多
    逃逸分析
    1. 不是所有的变量都放在协程栈上,栈帧回收后,需要继续使用的变量,或者 太大的变量,分为指针逃逸,空接口逃逸和大变量逃逸,从栈逃逸分配到堆空间上
    • 指针逃逸 (函数返回的指针被其他使用)
    func a() *int {
    	v :=0
    	return &v // 导致 局部变量会分配在堆行 不会分配栈上
    }
    
    • 1
    • 2
    • 3
    • 4
    • 空接口逃逸(函数的参数是interface{} 函数的实参很可能会逃逸,主要因为interface{} 类型函数往往会使用反射
    fmt.Println(i) // 入参属于interface{} 空接口, 是否有反射查看值是什么类型 逃逸到堆上
    
    • 1
    • 大变量逃逸(过大变量导致的空间不足,超过64KB的变量会逃逸)
    // 解决协程的栈空间不足
    
    • 1
    • 调用栈帧太多(栈扩容
    2. 解决方式 进行栈扩容,Go的栈初始空间为2KB
    3. 在函数调用前判断栈空间morestack
    4. 早期使用分段栈(go 1.13) 在逻辑上连接,优点没有空间浪费,栈指针会在不连续的空间跳转,后期 连续栈,缺点 伸缩时候开销大,扩容为原来2, 使用比例不足1/4, 变为原来的 1/2 
    
    • 1
    • 2
    • 3

    go 堆内存

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

    1. go 使用 heapArena每次申请虚拟内存单元 64MB,所有的heapArena 组成 堆内存
      在这里插入图片描述
    • 线性分配据或者链表分配出现空间碎片,所有go 语言中使用分级分配,避免内存的碎片化,每个内存进行分级思想, mspan n内存管理单元
      在这里插入图片描述

    在这里插入图片描述
    按照需求进行分级分配,runtime.sizeclass.go 进行分配, 总共有68 个级别。
    在这里插入图片描述
    其中 136 个span , mcentral 属于链接头,其中 68个需要GC扫描,其他68个不需要GC扫描。
    mcentral 的属于中心索引,使用互斥锁保护,在高并发的场景下 锁冲突严重,参考GMP模型,增加线程的本地缓存。

    • Go 模仿TCmalloc ,建立自己的堆内存架构
    • 使用heapArena 向操作系统申请内存,以mspan 为单位,防止碎片化
    • mcentral 是mspan 的中心索引
    • 使用mcache 本地缓存 大大降低 锁竞争问题
    堆如何进行分配
    • Tiny 微对象(0,16B)无指针 – 分配到普通mspan(class 1 - class 67) --将多个微级对象合并成16Byte
    • Small 对象[16B, 32K] – 定制作mspan (class 0)
    • Large 大对象(32KB, +)
    • heapArena 不足的化 会自动申请扩容

    go 语言对象的垃圾回收

    • 标记-清除
    • 标记-整理 (go 语言使用分级分配 不需要标记整理)
    • 标记-复制 (只有用内存进行复制,但是空间浪费非常大,Java的新生代)
      总结: Go 堆内存的独特方式 进行标记清除掉,如何寻找有用?
    • Gc的起点: 1. 被栈上的指针引用 2.被全局变量引用 3.被寄存器中指针引用
    • Root 节点进行广度优先策略进行搜索(可达性分析标记方法)
    • 暂停所有其他协程,进行可达性分析,找到无引用的GC 属于串行GC 属于在OLD version 中

    如何减少GC对性能的分析

    • 如何进行并行GC 提升性能?
      难点在于如何进行标记阶段,go 语言采用的 三色标记方法
      ·- 黑色: 表示已经分析扫描,有用
      ·- 灰色: 有用,还没进行分析扫描 DFS代替队列
      ·- 白色: 暂时无用
      三色标记方法
      当三色标记结束后只有黑色的对象,下一次开启恢复成 白色

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

    • Yuasa 删除屏障(s释放的指针进行强制为灰色
      在这里插入图片描述1. 删除屏障可以杜绝在GC标记中删除的问题 ,但是也无法解决并发标记的插入问题
      在这里插入图片描述
      针对插入屏障 使用 Dijkstra 插入屏障
      并发标记过程中 将C进行强制置灰,当并发标记过程,新指针指向新的对象,新增的依赖对象 防止错误的GC

    • 混合屏障

    1. 被删除的堆对象标记成为灰色
    2. 被添加的堆对象标记成为灰色
      并发垃圾回收关键在于标记安全,兼顾的安全的效率

    GC 优化效率

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

    • 用户显示触发
      调用runtime.gc 并不推荐

    • 申请内存触发
      给申请对象的时候伴随着GC

    • GC优化原则

    1. 尽量少在堆上产生垃圾
    2. 内存池化(channel 中 环形池)
    3. 减少逃逸 (fmt 包, 返回了指针不是拷贝)
    4. 使用空结构体 (不占用空结构体,使用channel 传递空结构体)
      使用如下的方式 查看内存
    $env:GODEBUG="gctrace=1"
    
    • 1
  • 相关阅读:
    数据库——知识1
    海贼王大学生HTML网页制作作品 学生动漫网页设计模板下载 简单漫画网页设计成品 dreamweaver学生网站模板
    使用 Python 进行敏感词过滤
    移动通信:分集技术(时间分集,频率分集,空间分集,SC,MRC,EGC)学习笔记
    18--Django-项目实战-博客开发-个人站点板块
    05-Nginx性能调优
    Milvus向量数据库:高效处理海量非结构化数据的利器
    电商平台的支付解决方案
    葡聚糖-叠氮|Dextran-Azide|叠氮修饰的葡聚糖|Dextran-N3|葡聚糖-聚乙二醇-叠氮
    i711800h和i511320h哪个好
  • 原文地址:https://blog.csdn.net/qq_27217017/article/details/126807349