美文网首页
Go的内存管理

Go的内存管理

作者: 绝望的祖父 | 来源:发表于2018-03-05 17:11 被阅读84次

本文翻译自Memory Management in Go,介绍了Go语言中内存管理的相关概念。

所有的计算机程序语言都必须处理内存管理,本文将讨论Go语言使用的一些内存管理概念。

堆栈,堆和数据段

在计算机环境中,有三个地方可以分配内存:堆栈,堆和数据段。

堆栈

一部分内存分配是在堆栈上完成的。堆栈拥有一个可以上下移动的顶部,通过向上移动栈顶可以在堆栈上分配空间,并且可以通过将栈顶向下移动来释放空间。栈顶是一个内存地址,可以通过快速的算术运算进行递增或递减。

通常,函数参数和局部变量在堆栈上进行分配。

每一个 goroutine 都拥有自己的堆栈,因此不需要同步(例如加锁)。

Goroutine 的堆栈在堆上进行分配。如果堆栈需要的内存大小超出分配给它的大小,那么将会发送堆操作(分配新的空间,将旧空间的内容复制到新空间中,释放旧空间)

还有一部分内存空间被分配在堆上。与堆栈不同的是,堆并没有一个单个的区域用来分配或者释放。相反,堆中包含一组空闲的区域,必须使用某种数据结构来管理这组空闲区域(一般来说是一棵二叉树)。当在堆上进行内存分配时,将会从这片空闲区域中删除某个结点,当这块内存需要释放时,又会将它添加回这组空闲区域中。

与堆栈不同,堆不属于某个 goroutine, 因此操作堆中空闲区域的集合时需要进行同步(例如锁定)。

数据段

内存也可以在数据段中分配。这是全局变量的存储位置。数据段在程序编译时确定,因此在运行时不会增长或缩小。

将在何处分配内存

Go程序语言规范并未定义将会在何处分配内存。例如,定义为var x int的变量可以分配在堆栈或堆上,并且仍然遵循语言规范。同样,由p := new(int)定义的整形指针也可以分配在堆栈或堆上。

但是,某些既定要求会在某些条件下排除一些选项,例如:

  • 数据段的大小在运行时不能改变,因此不能用于可改变大小的数据结构
  • 某些在堆栈中的项的生命周期会按照其在堆栈中的位置进行排序,如果栈顶地址是X,那么在X之上的所有内容都将被释放,而X以下的所有内容将被保留。由函数分配的内存项如果被函数作用域范围外的项引用,那么它将"逃逸"出这个函数,因此不能在堆栈上进行分配(因为它仍然被引用),并且也不能在数据段上分配(因为数据段在运行时无法增长),所以它只能在堆上进行分配 - 虽然内联可以减少其中的一些堆分配。

逃逸分析

逃逸分析用来确定内存项是否可以在堆栈上进行分配。它确定一个函数中创建的项目(例如局部变量)是否可能逃逸出该函数,或者到其他 goroutine 中。例如,在下面的函数中,x 从定义它的函数中逃逸出来:

package escapeanalysis
 
 func Foo() *int {
     var x int
     return &x
 }

可能逃逸的项必须分配在堆上,因此 x 将被分配在堆上。

确切的逃逸分析算法可能在不同版本的Go中发生改变。但是,你可以使用go tool compile -m来打印优化策略,其中包含逃逸分析。例如,前面的程序在Go 1.8.3 版本中会得到如下输出:

escape.go:3: can inline Foo
escape.go:5: &x escapes to heap
escape.go:4: moved to heap: x

垃圾回收

Go的内存管理支持垃圾收集。Go的垃圾收集器为了完成任务,偶尔不得不STW(stop the world)。自从1.5版本以来,收集器的设计使得在50毫秒的执行时间内STW任务的时间不会超过10毫秒。

垃圾收集器必须知道堆和堆栈中分配的项目。很容易看出,如果你发现一个堆中分配的项目H,引用了一个堆栈中分配的项目S,很明显,垃圾收集器将不会释放H,直到S被释放掉,因此,垃圾收集器必须知道S的生命周期。

性能

如果你的程序是CPU密集型,请使用 runtime/pprofgo tool pprof来分析程序。如果你发现像 growslice 或 newobject 这样的符号占用了大量时间,那么优化内存分配可能会提高性能。

假设你已经确定优化内存分配可以提高程序性能,那么请减少分配的数量 - 尤其是堆分配。

  1. 重用已分配的内存
  2. 重构你的代码,以便编译器可以进行堆栈分配而不是堆分配。使用go tool compile -m来帮助你识别将被分配到堆上的逃逸变量,然后重写你的代码以便可以进行堆栈分配。
  3. 重构你的CPU密集型代码,以便可以在几个大内存块中进行内存分配,而不是连续分配小块的内存。

相关文章

  • Go语言——内存管理

    Go语言——内存管理 参考: 图解 TCMalloc Golang 内存管理 Go 内存管理 问题 内存碎片:避免...

  • 图解 Go 内存管理器的内存分配策略

    关于Go的内存分配 在 Go 语言里,从内存的分配到不再使用后内存的回收等等这些内存管理工作都是由 Go 在底层完...

  • go 内存模型简要说明

    go 内存模型 大体上来说go的内存是先申请一大片内存,然后将内存分为各个小的span来管理,因为每个go对象有对...

  • Go 语言内存管理(一):系统内存管理

    介绍 要搞明白 Go 语言的内存管理,就必须先理解操作系统以及机器硬件是如何管理内存的。因为 Go 语言的内部机制...

  • Go 语言内存管理(二):Go 内存管理

    介绍 了解操作系统对内存的管理机制后,现在可以去看下 Go 语言是如何利用底层的这些特性来优化内存的。Go 的内存...

  • GO 内存管理

    要搞明白 Go 语言的内存管理,就必须先理解操作系统以及机器硬件是如何管理内存的。因为 Go 语言的内部机制是建立...

  • go内存管理

    1. Go 内存的划分 强烈推荐参考链接 在讲Go的堆栈之前,先温习一下堆栈基础知识。 什么是堆栈?在计算机中堆栈...

  • go 内存管理

    1. 内存分配步骤 go 给对象分配内存的主要流程: object size > 32K,则使用 mheap 直接...

  • go内存管理

    这篇文章可以看作是内存管理这篇长文的学习总结吧,原文基于源码剖析了整个go的内存管理,非常详尽。 程序中的数据和变...

  • Go内存管理

    https://www.cnblogs.com/shijingxiang/articles/11466957.ht...

网友评论

      本文标题:Go的内存管理

      本文链接:https://www.haomeiwen.com/subject/dehhfftx.html