美文网首页Go语言实践Go
go-内存机制(5)

go-内存机制(5)

作者: GGBond_8488 | 来源:发表于2020-03-29 10:52 被阅读0次

总结

Go的内存结构

每个Go程序进程都由操作系统(OS)预先分配了一些虚拟内存,这是该进程可以访问的全部内存。在这个虚拟内存中实际正在使用的内存称为Resident Set(驻留内存)。该空间由内部内存结构管理,如下所示:


mheap(堆)

这里是Go存储动态数据(在编译时无法计算大小的任何数据)的地方。它是最大的内存块,也是进行垃圾收集(GC)的地方
驻留内存(resident set)被划分为每个大小为8KB的页,并由一个全局mheap对象管理。

大对象(大小> 32kb的对象)直接从mheap分配。这些大对象申请请求是以获取中央锁(central lock)为代价的,因此在任何给定时间点只能满足一个P的请求。

mheap通过将页归类为不同结构进行管理的:

  • mspan:mspan是mheap中管理的内存页的最基本结构。这是一个双向链接列表,其中包含起始页面的地址,span size class和span中的页面数量。Go将内存页按大小分为67个不同类别,大小从8字节到32KB,见内存机制3.
    每个span存在两个,一个span用于带指针的对象(scan class),一个用于无指针的对象(noscan class)。这在GC期间有帮助,因为noscan类查找活动对象时无需遍历span。

  • mcentral:mcentral将相同大小级别的span归类在一起。每个mcentral包含两个mspanList:
    •empty:双向span链表,包括没有空闲对象的span或缓存mcache中的span。当此处的span被释放时,它将被移至non-empty span链表。
    •non-empty:有空闲对象的span双向链表。当从mcentral请求新的span,mcentral将从该链表中获取span并将其移入empty span链表。
    如果mcentral没有可用的span,它将向mheap请求新页。

  • arena:堆在已分配的虚拟内存中根据需要增长和缩小。当需要更多内存时,mheap从虚拟内存中以每块64MB(对于64位体系结构)为单位获取新内存, 这块内存被称为arena。这块内存也会被划分页并映射到span。

  • mcache:这是一个非常有趣的构造。mcache是提供给P(逻辑处理器)的高速缓存,用于存储小对象(对象大小<= 32Kb)。尽管这类似于线程堆栈,但它是堆的一部分,用于动态数据。所有类大小的mcache包含scan和noscan类型mspan。Goroutine可以从mcache没有任何锁的情况下获取内存,因为一次P只能有一个锁G。因此,这更有效。mcache从mcentral需要时请求新的span。

stack(栈)

栈存储区,每个Goroutine(G)有一个栈。在这里存储了静态数据,包括函数栈帧,静态结构,原生类型值和指向动态结构的指针。

与许多垃圾回收语言相比,Go的一个主要区别是许多对象直接在程序栈上分配。Go编译器使用一种称为"逃逸分析"的过程来查找其生命周期在编译时已知的对象,并将它们分配在栈上,而不是在垃圾回收的堆内存中。在编译过程中,Go进行了逃逸分析,以确定哪些可以放入栈(静态数据),哪些需要放入堆(动态数据)。我们可以通过运行带有-gcflags '-m'标志的go build命令来查看分析的细节

  • main函数被保存栈中的“main栈帧”中
  • 每个函数调用都作为一个栈帧块被添加到堆中
  • 包括参数和返回值在内的所有静态变量都保存在函数的栈帧块内
  • 无论类型如何,所有静态值都直接存储在栈中。这也适用于全局范畴
  • 所有动态类型都在堆上创建,并且被栈上的指针所引用。小于32Kb的对象由P的mcache分配。这同样适用于全局范畴
  • 具有静态数据的结构体保留在栈上,直到在该位置将任何动态值添加到该结构中为止。该结构被移到堆上。
  • 从当前函数调用的函数被推入堆顶部
  • 当函数返回时,其栈帧将从栈中删除
  • 一旦主过程(main)完成,堆上的对象将不再具有来自Stack的指针的引用,并成为孤立对象

栈是由操作系统自动管理的,而不是Go本身。因此,我们不必担心栈。另一方面,堆并不是由操作系统自动管理的,并且由于其具有最大的内存空间并保存动态数据,因此它可能会成倍增长,从而导致我们的程序随着时间耗尽内存。随着时间的流逝,它也变得支离破碎,使应用程序变慢。解决这些问题是垃圾收集的初衷。

内存管理

Go的内存管理包括在需要内存时自动分配内存,在不再需要内存时进行垃圾回收。

内存分配

Go根据对象的大小决定对象的分配过程,分为三类:

微小对象(Tiny)(size <16B):使用mcache的微小分配器分配大小小于16个字节的对象。这是高效的,并且在单个16字节块上可完成多个微小分配。

image

小对象(尺寸16B〜32KB):大小在16个字节和32k字节之间的对象被分配在G运行所在的P的mcache的对应的mspan size class上。

image
在微小型和小型对象分配中,如果mspan的列表为空,分配器将从mheap获取大量的页面用于mspan。如果mheap为空或没有足够大的页面满足分配请求,那么它将从操作系统中分配一组新的页(至少1MB)

大对象(大小> 32KB):大于32 KB的对象直接分配在mheap的相应大小类上(size class)。如果mheap为空或没有足够大的页面满足分配请求,则它将从操作系统中分配一组新的页(至少1MB)。

image

垃圾回收(GC)

Go通过垃圾回收机制管理堆内存。简单来说,它释放了孤儿对象(orphan object)使用的内存,所谓孤儿对象是指那些不再被栈直接或间接(通过另一个对象中的引用)引用的对象,从而为创建新对象的分配腾出了空间。

Go 1.12版本开始,Go使用了非分代的、并发的、基于三色标记和清除的垃圾回收器。

当完成一定百分比(GC百分比)的堆分配,GC过程就开始了。收集器将在不同工作阶段执行不同的工作:

  • 标记设置(mark setup, stw):GC启动时,收集器将打开写屏障(write barrier),以便可以在下一个并发阶段维护数据完整性。此步骤需要非常小的暂停(stw),因此每个正在运行的Goroutine都会暂停以启用此功能,然后继续。
  • 标记(并发执行的):打开写屏障后,实际的标记过程将并行启动,这个过程将使用可用CPU能力的25%(用于GC的后台任务不超过25%)。对应的P将保留,直到该标记过程完成。这个过程是使用专用的Goroutines完成的。在这个过程中,GC标记了堆中的活动对象(被任何活动的Goroutine的栈中引用的)。当采集花费更长的时间时,该过程可以从应用程序中征用活动的Goroutine来辅助标记过程。这称为Mark Assist。(辅助GC)
  • 标记终止(stw):标记一旦完成,每个活动的Goroutine都会暂停,写入屏障将关闭,清理任务将开始执行。GC还会在此处计算下一个GC目标。完成此操作后,保留的P的会释放回应用程序。
  • 清除(并发):当完成收集并尝试分配后,清除过程开始将未标记为活动的对象回收。清除的内存量与分配的内存量是同步的(即回收后的内存马上可以被再分配了)。

参考

可视化讲解 Go 内存管理

《Visualizing memory management in Golang》

相关文章

  • go-内存机制(5)

    总结 Go的内存结构 每个Go程序进程都由操作系统(OS)预先分配了一些虚拟内存,这是该进程可以访问的全部内存。在...

  • go-内存机制(2)

    逃逸对性能的影响 在(1)中,通过一个共享在 goroutine 的栈上的值的例子讲解了逃逸分析的基础。还有其他没...

  • go-内存机制(1)

    逃逸分析 堆与栈 在go语言中,变量可以存储在栈或者堆之上。如果变量存储在栈之上,那么当这个栈被清理时,对应的栈内...

  • go-内存机制(4)

    go的GC机制 GO的GC是并行GC,也就是说GC的大部分清理和普通的go代码是同时运行的,这让GO的GC流程比较...

  • go-内存机制(3)

    go的内存分配 Golang有一套自己的内存管理机制,自主的去完成内存分配、垃圾回收、内存管理等过程,从而避免频繁...

  • go的协程并发-channel消息机制

    go的协程并发-channel消息机制 方式一 方式二 go-协程实现方案汇总

  • go-锁机制

    Golang中的锁机制主要包含互斥锁和读写锁 互斥锁 互斥锁是一种简单的加锁的方法来控制对共享资源的访问,互斥锁只...

  • 性能优化<第五篇>:内存优化

    1、为什么要内存优化? 2、了解内存中的内容 3、内存上限 4、GC内存回收机制 5、内存泄漏、内存抖动、内存溢出...

  • python内存管理机制

    Python内存管理机制 Python内存管理机制主要包括以下三个方面: 引用计数机制 垃圾回收机制 内存池机制 ...

  • 谈谈js中的内存机制——垃圾回收机制

    内存管理机制就是分配内存管理,每种编程语言都有它的内存管理机制,JavaScript的内存管理机制是:内存基元在变...

网友评论

    本文标题:go-内存机制(5)

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