GopherCon SG 2019 “Understanding Allocations” 学习笔记

本篇是根据 GopherCon SG 2019 “Understanding Allocations” 演讲的学习笔记。

Understanding Allocations: the Stack and the Heap – GopherCon SG 2019 – YouTube

理解分配:栈和堆

GopherCon SG 2019 "Understanding Allocations" 学习笔记

你的程序中有两种内存,栈内存和堆内存。

go 中, 每个 go 程都会有一个栈空间,整个程序有一个堆空间。

变量是在栈还是堆上

GopherCon SG 2019 "Understanding Allocations" 学习笔记

负责堆垃圾回收的 GC 会导致整个程序的延迟,而不仅仅是创建垃圾的部分。你可能会担心你的代码在堆中产生了多少垃圾。

什么时候需要优化

GopherCon SG 2019 "Understanding Allocations" 学习笔记

要有 benchmarks 基准来证明你的程序 不够快(有大量的堆内存分配),够快就不用多此一举了。

你要先确保程序能正确运作(业务处理),而不是先着重性能优化。

在有大量指针+运行快速的程序、清晰明了+但有点慢的程序之间,你应该选择后者。

普通类型参数传递

第5行进入squre函数

GopherCon SG 2019 "Understanding Allocations" 学习笔记

函数和局部变量被挤压入栈,一个函数为一个 堆栈帧

squre函数执行完

GopherCon SG 2019 "Understanding Allocations" 学习笔记

执行完成后,你会发现黑线(只是用于区分)向上移,上方内容为有效内容,下方内容为无效内容( 过期而不再使用

第6行执行println函数

GopherCon SG 2019 "Understanding Allocations" 学习笔记

go 声明了新的内存部分,我们有了新的堆栈帧用于打印行,黑线下移。

通俗的来讲,栈空间会进行自我清理,任何变量都会被清理干净,空间会被重复使用。

指针类型参数传递

第4行声明变量

GopherCon SG 2019 "Understanding Allocations" 学习笔记

main 函数和变量 n 被压入栈。

第5行进入inc函数

GopherCon SG 2019 "Understanding Allocations" 学习笔记

inc 函数和变量 x 被压入栈,属于另一个堆栈帧,黑线下移。传入的变量是 n 的地址。

inc函数执行完

GopherCon SG 2019 "Understanding Allocations" 学习笔记

黑线上移,n 被修改了值。并没有什么问题。

执行println函数

GopherCon SG 2019 "Understanding Allocations" 学习笔记

总结

虽然使用了指针传参,但这种情况下它能够留在栈上,即共享向下传递时,通常留在栈空间上。(主函数将自己的变量传给子函数)

函数返回引用

第4行进入answer函数前

GopherCon SG 2019 "Understanding Allocations" 学习笔记

编译器推断出 n 是 int 的指针类型,赋初值 nil 等待函数返回结果。

进入函数

GopherCon SG 2019 "Understanding Allocations" 学习笔记

在函数内部声明了变量并初始化,并返回了变量的内存地址。

执行println函数

GopherCon SG 2019 "Understanding Allocations" 学习笔记

致命的问题

你会发现,我明明没有进行赋值修改的操作,但却破坏了原有的值。原因在于

  • 调用 answer 函数的时候,假如我们把声明的变量 x 放在栈空间,返回变量的地址。
  • 那么 main 函数中的 n 会拿到 x 的地址,当 answer 方法退出时(黑线上移), 该堆栈帧已过期,但是 n 还引用着 x
  • 我们知道 堆栈的空间是可以被重复利用的,那么下一次执行其他函数或者声明变量的时候,那块过期的内存区域就会被重新使用,修改为其他值。
  • 案例中调用 println,黑线下移,产生新的堆栈帧,原先 x 标识符被替换成了 a,修改 a 的值等同修改了 n 地址解引用后的值。这是非常致命的错误。

解决的方法

GopherCon SG 2019 "Understanding Allocations" 学习笔记

为了解决上述致命的问题,被声明初始化的 x 变量就需要”逃逸”到堆空间中。这样其他无关函数就不会对它的值产生影响。

共享向上传递时,通常留在堆空间上。(子函数将自己的变量传给主函数,引用)

Escape Analysis

编译器怎么判断的

GopherCon SG 2019 "Understanding Allocations" 学习笔记

GopherCon SG 2019 "Understanding Allocations" 学习笔记

变量只在函数里工作,那就分配到栈空间上。如果编译器不能证明函数返回后声明过的变量是否被引用,那必须将其分配到堆空间上。

询问编译器

GopherCon SG 2019 "Understanding Allocations" 学习笔记

查看构建指令,可以提供一个 -gcflags 的可选参数,传递给 go tool compile 工具

GopherCon SG 2019 "Understanding Allocations" 学习笔记

查询 go tool compile -h,得知参数 -m 可以输出编译器的优化决定(变量放在栈还是堆上)

GopherCon SG 2019 "Understanding Allocations" 学习笔记

执行 go build -gcflags “-m” 获取到下面的结果

GopherCon SG 2019 "Understanding Allocations" 学习笔记

GopherCon SG 2019 "Understanding Allocations" 学习笔记

总结

GopherCon SG 2019 "Understanding Allocations" 学习笔记

GopherCon SG 2019 "Understanding Allocations" 学习笔记

什么时候会把变量分配在堆内存上

  • 函数返回退出后声明过的变量依旧被引用着。
  • 变量初始化值大小过大无法分配进栈。
  • 不知道变量值的具体大小,比如切片。

做个判断

GopherCon SG 2019 "Understanding Allocations" 学习笔记

看看前面总结的第一点和第三点。明显可以知道右边那个是分配在栈上,而左边那个分配在堆上。

思考io.Reader接口

GopherCon SG 2019 "Understanding Allocations" 学习笔记

io 标准库里有个 Reader 接口,你应该知道为什么官方要用前者替换后者了吧。如果使用后者,会在堆上产生大量的垃圾,造成程序迟钝。前者则符合向下传递思想,变量通常分配在栈空间上。

最后

GopherCon SG 2019 "Understanding Allocations" 学习笔记

不要猜想,多用工具!

Original: https://www.cnblogs.com/linxiaoxu/p/16095012.html
Author: 小能日记
Title: GopherCon SG 2019 “Understanding Allocations” 学习笔记

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/516380/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 【golang】pprof性能调优工具的具体使用(带案例)

    前言 大晚上的,老是刷到有关pprof的文章,忍不住看了几篇文章…写个学习笔记记录下~ 正文: 1.pprof是什么? pprof是go内置的性能调优工具,可以借助一些…

    Go语言 2023年5月25日
    070
  • go 连接MSSQLServer数据库【遇到的坑】

    前言:项目测试需要用到mssqlserver数据库连接,遇到坑,自己爬 直接上代码: go;gutter:true; package main</p> <p&gt…

    Go语言 2023年5月25日
    047
  • Go语言之网络编程

    一、网络编程基础 网络基础之TCP/IP协议族 网络编程之socket 二、TCP Socket编程 (一)流程 首先应该了解服务端和客户端的处理流程: 1、服务端处理流程 监听端…

    Go语言 2023年5月29日
    059
  • Go汇编语法和MatrixOne使用介绍

    目录 MatrixOne数据库是什么? Go汇编介绍 为什么使用Go汇编? 为什么不用CGO? Go汇编语法特点 操作数顺序 寄存器宽度标识 函数调用约定 对写Go汇编代码有帮助的…

    Go语言 2023年5月25日
    085
  • Go语言实践模式 – 函数选项模式(Functional Options Pattern)

    什么是函数选项模式 大家好,我是小白,有点黑的那个白。 最近遇到一个问题,因为业务需求,需要对接三方平台. 而三方平台提供的一些HTTP(S)接口都有统一的密钥生成规则要求. 为此…

    Go语言 2023年5月25日
    036
  • 编译kubeadm使生成证书有效期为100年

    问题 编译 检查结果 问题 当我使用kubeadm部署成功k8s集群时在想默认生成的证书有效期是多久,如下所示 /etc/kubernetes/pki/apiserver.crt …

    Go语言 2023年5月25日
    051
  • [原创]Golang一行代码给钉钉群推送消息

    [原创]Golang一行代码给钉钉群推送消息 钉钉本来就是工具,只是boss把你变成了工具. — 麦·卡隆 今天朋友扔给我个某签到脚本,让我做推送功能. 我迅速从吃灰收藏夹里掏出S…

    Go语言 2023年5月25日
    042
  • 基于知名微服务框架go-micro开发gRPC应用程序

    go-micro是golang的一个微服务框架。 go-micro各个版本之间的兼容性问题一直被诟病,前几年go-micro更是分化出了两个分支: 一个延续了go-micro,只不…

    Go语言 2023年5月25日
    0128
  • 将百度万年历存入自己的数据库

    前言 最近有需要研究阴历和阳历互相转换的问题。因此找到两个库carbon和solarlunar但是感觉计算出来的总是不太放心,而且也会占用计算资源。我的想法是通过接口获取现成的阴历…

    Go语言 2023年5月25日
    058
  • [grpc快速入门] 一 grpc生成与调用

    下载通用编译器 地址:https://github.com/protocolbuffers/protobuf/releases选择对应的版本,解压后将文件夹下bin目录配置到环境变…

    Go语言 2023年5月25日
    079
  • 对不起,我错了,这代码不好写

    hello,大家好呀,我是小楼。 前几天不是写了这篇文章《发现一个开源项目优化点,点进来就是你的了》嘛。 文章介绍了Sentinl的自适应缓存时间戳算法,从原理到实现都手把手解读了…

    Go语言 2023年5月25日
    061
  • 【golang详解】go语言GMP(GPM)原理和调度

    Goroutine调度是一个很复杂的机制,下面尝试用简单的语言描述一下Goroutine调度机制,想要对其有更深入的了解可以去研读一下源码。 首先介绍一下GMP什么意思: G &#…

    Go语言 2023年5月25日
    059
  • Minio SDK访问Bucket的策略配置

    配置用户来访问 Bucket Minio 是高性能的对象存储服务,基于golang开发的,可以本地部署。用它来管理自己系统中的上传下载的文件很方便。​ 通过 SDK 访问 Mini…

    Go语言 2023年5月25日
    077
  • DDIA 学习笔记

    第一章 可靠性、可扩展性、可维护性 ​ 可靠性: 系统在 困境(adversity)(硬件故障、软件故障、人为错误)中仍可正常工作(正确完成功能,并能达到期望的性能水准。 ​ 可靠…

    Go语言 2023年5月25日
    070
  • Go语言之反射

    一、反射的基本概念 (一)什么是反射 反射可以再运行时动态获取变量的各种信息,比如变量的类型、值等 如果时结构体变量,还可以获取到结构体本身的各种信息,比如结构体的字段、方法 通过…

    Go语言 2023年5月29日
    069
  • Golang – 关于 proto 文件的一点小思考

    ProtoBuf 是什么? ProtoBuf 是一套接口描述语言(IDL),通俗的讲是一种数据表达方式,也可以称为数据交换格式。 我们常用的数据格式有 JSON 和 XML,为什么…

    Go语言 2023年5月25日
    057
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球