![Illustration created for “A Journey With Go”, made from the original Go Gopher, created by Renee French.](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20191002-Go-Keeping-a-Variable-Alive/00.png)

本文基于 Go 1.13。

在 Go 中,我们不需要自己管理内存分配和释放。然而,有些时候我们需要对程序进行更细粒度的控制。Go 运行时提供了很多种控制运行时状态及其与内存管理器之间相互影响的方式。本文中,我们来审查让变量不被 GC 回收的能力。

## 场景

我们来看一个基于 [Go 官方文档](https://golang.org/pkg/runtime/#KeepAlive) 的例子:

```go

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

}

```

[源码地址](https://gist.githubusercontent.com/blanchonvincent/a247b6c2af559b62f93377b5d7581b7f/raw/6488ec2a36c28c46f942b7ac8f24af4e75c19a2f/main.go)

这个程序中一个函数打开文件,另一个函数读取文件。代表文件的结构体注册了一个 finalizer,在 gc 释放结构体时自动关闭文件。运行这个程序,会出现 panic:

```bash

panic: bad file descriptor

goroutine 1 [running]:

main.readFile(0x3, 0x5, 0xc000078008)

main.go:42 +0x103

main.main()

main.go:14 +0x4b

exit status 2

```

下面是流程图:

- 打开文件,返回一个文件描述符

- 这个文件描述符被传递给读取文件的函数

- 这个函数首先做一些繁重的工作:

![图 01](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20191002-Go-Keeping-a-Variable-Alive/01.png)

allocate 函数触发 gc:

![02.png](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20191002-Go-Keeping-a-Variable-Alive/02.png)

因为文件描述符是个整型,并以副本传递,所以打开文件的函数返回的结构体 `*File*` 不再被引用。Gc 把它标记为可以被回收的。之后触发这个变量注册的 finalizer,关闭文件。

然后,主协程读取文件:

![03.png](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20191002-Go-Keeping-a-Variable-Alive/03.png)

因为文件已经被 finalizer 关闭,所以会出现 panic。

## 让变量不被回收

`runtime` 包暴露了一个方法,用来在 Go 程序中避免出现这种情况,并显式地声明了让变量不被回收。在运行到这个调用这个方法的地方之前,gc 不会清除指定的变量。下面是加了对这个方法的调用的新代码:

![04.png](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20191002-Go-Keeping-a-Variable-Alive/04.png)

在运行到 `keepAlive` 方法之前,gc 不能回收变量 `p`。如果你再运行一次程序,它会正常读取文件并成功终止。

## 追本逐源

`keepAlive` 方法本身没有做什么:

![05.png](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20191002-Go-Keeping-a-Variable-Alive/05.png)

运行时,Go 编译器会以很多种方式优化代码:函数内联,死码消除,等等。这个函数不会被内联,Go 编译器可以轻易地探测到哪里调用了 `keepAlive`。编译器很容易追踪到调用它的地方,它会发出一个特殊的 SSA 指令,以此来确保它不会被 gc 回收。

![06.png](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20191002-Go-Keeping-a-Variable-Alive/06.png)

在生成的 SSA 代码中也可以看到这个 SSA 指令:

![07.png](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20191002-Go-Keeping-a-Variable-Alive/07.png)

在我的文章 [Go 编译器概述](https://medium.com/a-journey-with-go/go-overview-of-the-compiler-4e5a153ca889) 中你可以看到更多关于 Go 编译器和 SSA 的信息。

本文由 GCTT 原创编译,Go语言中文网 荣誉推出

本文由 GCTT 原创翻译,Go语言中文网 首发。也想加入译者行列,为开源做一些自己的贡献么?欢迎加入 GCTT!

翻译工作和译文发表仅用于学习和交流目的,翻译工作遵照 CC-BY-NC-SA 协议规定,如果我们的工作有侵犯到您的权益,请及时联系我们。

欢迎遵照 CC-BY-NC-SA 协议规定 转载,敬请在正文中标注并保留原文/译文链接和作者/译者等信息。

文章仅代表作者的知识和看法,如有不同观点,请楼下排队吐槽

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

mysql 变量生命周期_Go: 延长变量的生命周期相关推荐

  1. Mysql基础运用(视图,变量,存储,流程控制)

    前要:结尾彩蛋 目录 一.视图 1.视图概述 2.创建视图 1)语法格式 2)视图示例 3.修改视图 1)语法格式 4.查看视图 5.删除视图 二.变量 1.变量分类 1)局部变量 2)用户变量 3) ...

  2. mysql 查询绑定变量_MySQL高级特性——绑定变量

    从MySQL 4.1 版本开始,就支持服务器端的绑定变量,这大大提高了客户端和服务器端数据传输的效率 介绍 当创建一个绑定变量 SQL 时,客户端会向服务器发送一个SQL语句的原型.服务器端收到这个S ...

  3. linux mysql makefile_Linux安装库文件(环境变量和makefile)

    CFLAGS 表示用于 C 编译器的选项, CXXFLAGS 表示用于 C++ 编译器的选项. 这两个变量实际上涵盖了编译和汇编两个步骤. CFLAGS/CPPFLAGS: 指定头文件(.h文件)的路 ...

  4. MySQL数据库变量_数据库参数_MySQL变量_系统变量_用户变量

    文章目录 MySQL 变量分类 系统变量 查看系统变量 设置系统变量 如何通过配置文件来设置变量值 通过命令行选项来设置变量值 动态设置全局级的系统变量 设置静态的系统变量 设置会话级的系统变量 引用 ...

  5. mysql jdbc 绑定变量_jdbc测试mysql数据库sql预解析(绑定变量)

    jdbc测试mysql数据库sql预解析(绑定变量) 用习惯了oracle,学习mysql,想测试一下mysql绑定变量的效果.以前看网上介绍大部份都说mysql没有sql共享池的概念,所以也不存在s ...

  6. mysql 变量定义和赋值_MySQL变量解析

    MySQL变量 MySQL变量分类: ①系统变量 全局变量 会话变量 ②自定义变量 用户变量 局部变量 1.系统变量 变量是由系统提供的,属于服务器层面,分全局变量和会话变量. 系统变量使用语法: 查 ...

  7. Mysql基础篇(8)—— 变量、流程控制和游标

    变量 变量分为系统变量和用户自定义变量 系统变量 由系统定义,属于服务器层面.这些系统变量定义了当前Mysql服务实例的属性.特征.系统变量分为全局系统变量(需要加global关键字)和会话级系统变量 ...

  8. MySQL环境变量的配置mysqldump环境变量的配置

    一.问题:安装Mysql之后,打开DOS窗口,直接输入mysqldump --help命令不被识别(mysqldump不是内部或外部命令,也不是可运行的程序或批处理文件) 二. 解决办法:配置MySQ ...

  9. MySQL InnoDB事务结构体代码变量全攻略(附源码)

    写在前面 ​ InnoDB是MySQL的一个存储引擎,支持事务,支持非堵塞的一致性读,物理存储结构是Page,每个事务都有回滚日志,重做日志,事务还会有死锁检测,各种各样不同的锁等等. 翻看InnoD ...

最新文章

  1. 独家 | 基于新闻标题的股价走势分析(附链接)
  2. 关于librtmp接收数据(接收网络电视的数据流)
  3. Android 怎样查看系统的memory swap 资讯/信息
  4. 如何在Kubernetes里创建一个Nginx应用
  5. zookeeper启动后没有相关进程
  6. Spring Cloud微服务笔记(四)客户端负载均衡:Spring Cloud Ribbon
  7. 一个牛逼的coder是这样诞生的。
  8. Codeforces Round #FF (Div. 2):Problem A - DZY Loves Hash
  9. python for a,b in c
  10. wr703n 4m固件 带打印机服务器_旗捷支招 | 如何关闭打印机固件自动更新?分分钟搞定!...
  11. SCM供应链管理系统介绍:企业SCM供应链系统应用领域、优势、功能详解
  12. Circular RNA的产生机制、功能及RNA-seq数据鉴定方法
  13. 洛谷 P3456 [POI2007]GRZ-Ridges and Valleys
  14. 启用计算机的无线同屏,win10系统无线同屏功能如何使用
  15. GNU Make 使用手册!
  16. 多个域名泛域名证书和多域名证书
  17. 转贴: 浅析多声道LPCM TRUE HD DTS HD之异同
  18. Bug[4] TCP挂机4.5小时左右自动断开,查询pdp显示未激活
  19. DiskPressure(磁盘压力)
  20. linux终端制表符长度,linux+制表符

热门文章

  1. 7类合作伙伴,190条沟通路径,高德汽车如何实现组织高效沟通?
  2. 如何使用 SQL Server FILESTREAM 存储非结构化数据?这篇文章告诉你!
  3. ps4看b站 f怎么调html5,b站html5,b站怎么切换到HTML5版播放器?
  4. 我要自学网java jsp_学javaweb需要什么基础?零基础如何学习javaweb?
  5. data.name.toLowerCase() is not a function问题
  6. editor.md 实现拖拽剪切复制粘贴上传图片,文件插件
  7. ElasticSearch docker安装
  8. Git 版本对比 idea图形化版本
  9. 关于Background-size的几个参数区别
  10. Vue - 去除控制台“你正在开发模式下运行Vue”的警告