Golang中的切片与GC

路过100 · · 1142 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

今天再看 timer 源码的时候,在函数 clearDeletedTimers() 里看到一段对切片的处理代码,实现目的就是对一个切片内容进行缩容。

// src/runtime/time.go

// The caller must have locked the timers for pp.
func clearDeletedTimers(pp *p) {
    timers := pp.timers
    ......
    // 对无用的切片元素赋值 nil
    for i := to; i < len(timers); i++ {
        timers[i] = nil
    }

    atomic.Xadd(&pp.deletedTimers, -cdel)
    atomic.Xadd(&pp.numTimers, -cdel)
    atomic.Xadd(&pp.adjustTimers, -cearlier)

    timers = timers[:to]
    pp.timers = timers
    updateTimer0When(pp)

    ......
}

to 变量指新切片的长度, len(timers)指原来切片的长度。

这里在其进行 timers = timers[:to] 操作前,先是将 to 数组索引后的值进行了赋值 nil。按照我们平时用用法,是没有必要执行这一步的?那为什么这里要加这一步呢,其实这里与GC 有关?

在日常开发中很少注意到这个细节,虽然最终实现的结果是一样的,但如果考虑GC的话,差别可就大多了。

假如这里不进行赋值nil 的话,结果是没有问题的,但我们知道slice是底层是由三部分组成的。

type slice struct {
    array unsafe.Pointer
    len   int
    cap   int
}

slice.array 字段是一个指向底层数组的指针,这里函数的参数 pp 是指 GPM 中的 P 结构体,pp.timers 字段类型为 timers []*timer

一个应用程序会长时间处于运行状态,当GC执行的时候,会发现 pp.timers 字段是slice类型,其值类型是一个指针类型,这个切片变量(数组)会一直存在于整个应用程序的生命周期。如果不先赋值 nil 的话,GC 扫描的时候,会发现这些指针元素仍处于被切片引用状态,这样就导致一直占用内存(已无用的数据);如果先赋值的话,则在GC在扫描对象时,会发现指针元素已经没有任何对象在引用,就会立即被标记并清除(三色标记清除法),回收这些指针对象的内存。

总结

在了解这种用法前,很有必要了解当前应用的执行上下文及环境,如果只是一个临时变量且生命周期很短的话,就没有必要这样做了,如一个函数的栈变量。这里的环境P是长期处于运行状态中的指针变量,所以很有必要进行手动赋值为nil, 等待对象被GC回收。


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:路过100

查看原文:Golang中的切片与GC

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1142 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传