首页
学习
活动
专区
圈层
工具
发布
50 篇文章
1
图文并茂VLAN详解,让你看一遍就理解VLAN
2
做了几年的网工也未必了解VLAN和VXLAN的区别,今天我来告诉你!
3
全网内容最全,质量最高的MPLS及MPLS VPN技术详解,瑞哥力荐!
4
Google BBR拥塞控制算法背后的数学解释 | 深度
5
QUIC 0-RTT实现简析及一种分布式的0-RTT实现方案
6
AGPS定位基本原理浅析
7
Golang语言情怀-第56期 Go 语言标准库翻译 crypto/cipher
8
神锁离线版插件端到端加密比HTTPS更安全
9
基于 TLS 1.3的微信安全通信协议 mmtls 介绍(上)
10
基于 TLS 1.3的微信安全通信协议 mmtls 介绍(下)
11
24位腾讯云专家精彩演讲,4万字《腾讯云技术实践精选集 2021》发布!(附合集下载)
12
浅谈VPC二三,秒懂秒透
13
提速 30%!腾讯TQUIC 网络传输协议
14
25 张图,一万字,拆解 Linux 网络包发送过程
15
nginx http模块数据存储结构
16
多机房多活,多机房平滑迁移架构方案全集(上+中+下)
17
小孩都看得懂的多臂老虎机和汤姆森采样
18
什么是 Go runtime.KeepAlive?
19
Rust 热点| Discord 为什么从 Go 切换到了 Rust
20
用户态 tcpdump 如何实现抓到内核网络包的?
21
一文读懂 | coredump文件是如何生成的
22
网工知识大扫盲——三层交换技术
23
聊聊 top 命令中的 CPU 使用率
24
什么是HDFS的纠删码
25
Linux ptrace 的实现
26
天天讲路由,那 Linux 路由到底咋实现的!?
27
Linux系统研究 - 操作系统是如何管理tcp连接的 (2)
28
一个有关tcp的非常有意思的问题
29
对上一篇文章中tcp问题的进一步思考
30
epoll和shutdown使用不当可能导致死循环
31
socket的SO_REUSEADDR参数全面分析
32
多进程可以监听同一端口吗
33
golang | 是返回struct还是返回struct的指针
34
​TCP 拥塞控制详解
35
C|网络|TCP-BBR拥塞控制剖析
36
如何使用 Go 语言写游戏服务器?
37
Nginx 日志 worker_connections are not enough while connecting to upstream
38
如何用九条命令在一分钟内检查Linux服务器性能?
39
服务器病了吗? Linux 服务器的那些性能参数指标
40
边缘计算比云计算强在哪里?终于有人讲明白了
41
详解边缘计算系统逻辑架构:云、边、端协同
42
边缘计算成为下一个爆发点,云计算巨头和CDN巨头谁会赢?
43
告知你不为人知的 UDP:连接性和负载均衡
44
为什么需要智能网卡?
45
从CDN到边缘计算,近水楼台是否先得月?
46
经典网络还是VPC,开发者作何选择?
47
Linux内核网络udp数据包发送(二)——UDP协议层分析
48
有没有人告诉你—写时拷贝的真相
49
[linux][network]net bridge技术分析
50
软硬件融合技术内幕 进阶篇 (1) —— 从小霸王到云计算

什么是 Go runtime.KeepAlive?

有些同学喜欢利用 runtime.SetFinalizer 模拟析构函数,当变量被回收时,执行一些回收操作,加速一些资源的释放。在做性能优化的时候这样做确实有一定的效果,不过这样做是有一定的风险的。

比如下面这段代码,初始化一个文件描述符,当 GC 发生时释放掉无效的文件描述符。

代码语言:javascript
复制

type File struct { d int }

func main() {
    p := openFile("t.txt")
    content := readFile(p.d)

    println("Here is the content: "+content)
}

func openFile(path string) *File {
    d, err := syscall.Open(path, syscall.O_RDONLY, 0)
    if err != nil {
      panic(err)
    }

    p := &File{d}
    runtime.SetFinalizer(p, func(p *File) {
      syscall.Close(p.d)
    })

    return p
}

func readFile(descriptor int) string {
    doSomeAllocation()

    var buf [1000]byte
    _, err := syscall.Read(descriptor, buf[:])
    if err != nil {
      panic(err)
    }

    return string(buf[:])
}

func doSomeAllocation() {
    var a *int

    // memory increase to force the GC
    for i:= 0; i < 10000000; i++ {
      i := 1
      a = &i
    }

    _ = a
}

上面这段代码是对 go 官方文档[1]的一个延伸,doSomeAllocation 会强制执行 GC,当我们执行这段代码时会出现下面的错误。

代码语言:javascript
复制
panic: no such file or directory

goroutine 1 [running]:
main.openFile(0x107a65e, 0x5, 0x10d9220)
        main.go:20 +0xe5
main.main()
        main.go:11 +0x3a

这是因为 syscall.Open 产生的文件描述符比较特殊,是个 int 类型,当以值拷贝的方式在函数间传递时,并不会让 File.d 产生引用关系,于是 GC 发生时就会调用 runtime.SetFinalizer(p, func(p *File) 导致文件描述符被 close 掉。

什么是 runtime.KeepAlive ?

如上面的例子,我们如果才能让文件描述符不被 gc 给释放掉呢?其实很简单,只需要调用 runtime.KeepAlive 即可。

代码语言:javascript
复制
func main() {
    p := openFile("t.txt")
    content := readFile(p.d)
    
    runtime.KeepAlive(p)

    println("Here is the content: "+content)
}

runtime.KeepAlive 能阻止 runtime.SetFinalizer 延迟发生,保证我们的变量不被 GC 所回收。

结论

正常情况下,runtime.KeepAlive,runtime.SetFinalizer 不应该被滥用,当我们真的需要使用时候,要注意使用是否合理。

《性能优化 | Go Ballast 让内存控制更加丝滑》我们说到如何让整个程序的声明周期内维护一个 slice 不被 gc 给回收掉,这里就用到了 runtime.KeepAlive。

这里还有有一篇关于 runtime.KeepAlive 的文档[2],有兴趣的可以看一下。

参考资料

[1]go 官方文档: https://pkg.go.dev/runtime#KeepAlive

[2]文档: https://medium.com/a-journey-with-go/go-keeping-a-variable-alive-c28e3633673a

下一篇
举报
领券