golang源码分析--gc

本文详细探讨了Go语言的垃圾回收(GC)原理,包括采用的标记-清理算法、为何选择三色标记清除法、内存泄漏的原因及形式、GC的触发条件、GC的执行流程和优化手段。同时,解释了GC安全点、写屏障和根对象的概念,以及GC与内存分配的关系。
摘要由CSDN通过智能技术生成

由于本人也属于小白学习,学习过程中也有很多不解的地方,欢迎大家提问,或者指出我未能讲到的部分,发现gc是一个很庞大的逻辑所以此篇会一直更新,到我觉得真的完全理解了再停更

golang概览及原理

golang的垃圾回收采用的是 标记-清理(Mark-and-Sweep) 算法
就是先标记出需要回收的内存对象快,然后在清理掉;
选取三色标记清除法的原因:
1.对象整理的优势是解决内存碎片问题以及“允许”使用顺序内存分配器。但 Go 运行时的分配算法基于 tcmalloc,基本上没有碎片问题。并且顺序内存分配器在多线程的场景下并不适用。Go 使用的是基于 tcmalloc 的现代内存分配算法,对对象进行整理不会带来实质性的性能提升。
2.分代 GC 依赖分代假设,即 GC 将主要的回收目标放在新创建的对象上(存活时间短,更倾向于被回收),而非频繁检查所有对象。但 Go 的编译器会通过逃逸分析将大部分新生对象存储在栈上(栈直接被回收),只有那些需要长期存在的对象才会被分配到需要进行垃圾回收的堆中。也就是说,分代 GC 回收的那些存活时间短的对象在 Go 中是直接被分配到栈上,当 goroutine 死亡后栈也会被直接回收,不需要 GC 的参与,进而分代假设并没有带来直接优势。并且 Go 的垃圾回收器与用户代码并发执行,使得 STW 的时间与对象的代际、对象的 size 没有关系。Go 团队更关注于如何更好地让 GC 与用户代码并发执行(使用适当的 CPU 来执行垃圾回收),而非减少停顿时间这一单一目标上。
有了GC为什么还会内存泄漏:
原因:预期的能很快被释放的内存由于附着在了长期存活的内存上,或生命期意外的被延长,导致预计能够立即回收的内存长时间得不到回收(由于goroutine还有多种形式)
形式1:预期能被快速释放的内存因被根对象引用而没有得到迅速释放:当有一个全局对象时,可能不经意间将某个变量附着在其上,且忽略的将其进行释放,则该内存永远不会得到释放

var cache = map[interface{
   }]interface{
   }{
   }

func keepalloc() {
   
  for i := 0; i < 10000; i++ {
   
    m := make([]byte, 1<<10)
    cache[i] = m
  }
}

形式2:goroutine 泄漏:

Goroutine 作为一种逻辑上理解的轻量级线程,需要维护执行用户代码的上下文信息。在运行过程中也需要消耗一定的内存来保存这类信息,而这些内存在目前版本的 Go 中是不会被释放的。因此,如果一个程序持续不断地产生新的 goroutine、且不结束已经创建的 goroutine 并复用这部分内存,就会造成内存泄漏的现象,


func keepalloc2() {
   
  for i := 0; i < 100000; i++ {
   
    go func() {
   
      select {
   }
    }()
  }
}

这种形式的 goroutine 泄漏还可能由 channel 泄漏导致。而 channel 的泄漏本质上与 goroutine 泄漏存在直接联系。Channel 作为一种同步原语,会连接两个不同的 goroutine,如果一个 goroutine 尝试向一个没有接收方的无缓冲 channel 发送消息,则该 goroutine 会被永久的休眠,整个 goroutine 及其执行栈都得不到释放,


var ch = make(chan struct{
   })

func keepalloc3() {
   
  for i := 0; i < 100000; i++ {
   
    // 没有接收方,goroutine 会一直阻塞
    go func() {
    ch <- struct{
   }{
   } }()
  }
}

gc的触发条件:
1.主动触发,通过调用 runtime.GC 来触发 GC,此调用阻塞式地等待当前 GC 运行完毕。
2.被动触发,分为两种方式:
2.1使用系统监控,当超过两分钟没有产生任何 GC 时,强制触发 GC。
2.2使用步调(Pacing)算法,其核心思想是控制内存增长的比例。
gc优化的手段
1.控制内存分配的速度,限制 goroutine 的数量,从而提高赋值器对 CPU 的利用率。
2.减少并复用内存,例如使用 sync.Po

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值