乐趣区

关于golang:Go语言轻松进阶

导读

本文基于 Go 源码版本 1.16、64 位 Linux 平台、1Page=8KB、本文的内存特指虚拟内存

明天咱们开始进入《Go 语言轻松进阶》系列第二章「内存与垃圾回收」第二局部「Go 语言内存治理」。

对于「内存与垃圾回收」章节,会从如下三大部分开展:

  • 读前常识储备(已完结)

    • 指针的大小
    • 内存的线性调配
    • 什么是 FreeList?
    • 虚拟内存
    • TCMalloc 内存调配原理
  • Go 语言内存治理(以后局部)
  • Go 语言垃圾回收原理(未开始)

第一局部「读前常识储备」曾经完结,为了更好了解本文大家能够点击历史链接进行查看或温习。

目录

对于解说「Go 语言内存治理」局部我的思路如下:

  1. 介绍整体架构
  2. 介绍架构设计中一个很有意思的中央
  3. 通过介绍 Go 内存治理中的要害构造 mspan,带出pagemspanobjectsizeclassspanclassheaparenachunk 的概念
  4. 接着介绍堆内存、栈内存的调配
  5. 回顾和总结

通过这个思路拆解的目录:

  • Go 内存治理架构(本篇内容)

    • mcache
    • mcentral
    • mheap
  • 为什么线程缓存 mcache 是被逻辑处理器 p 持有,而不是零碎线程m?
  • Go 内存治理单元mspan

    • page的概念
    • mspan的概念
    • object的概念
    • sizeclass的概念
    • spanclass的概念
    • heaparena的概念
    • chunk的概念
  • Go 堆内存的调配

    • 微对象调配
    • 小对象调配
    • 大对象调配
  • Go 栈内存的调配

    • 栈内存调配机会
    • 小于 32KB 的栈调配
    • 大于等于 32KB 的栈调配

Go 内存治理架构

Go 的内存对立由内存管理器治理的,Go 的内存管理器是基于 Google 本身开源的 TCMalloc 内存分配器为理念设计和实现的,对于 TCMalloc 内存分配器的具体介绍能够查看之前的文章。

先来简略回顾下 TCMalloc 内存分配器的外围设计。

回顾 TCMalloc 内存分配器

TCMalloc诞生的背景?

在多核以及超线程时代的明天,多线程技术曾经被宽泛使用到了各个编程语言中。当应用多线程技术时,因为 多线程共享内存 ,线程申在请内存(虚拟内存) 时,因为并行问题会产生竞争不平安。

为了保障分配内存的过程足够平安,所以须要在内存调配的过程中加锁,加锁过程会带来阻塞影响性能。之后就诞生了 TCMalloc 内存分配器并被开源。

TCMalloc如何解决这个问题?

TCMalloc全称 Thread Cache Memory alloc 线程缓存内存分配器。顾名思义就是给线程增加内存缓存,缩小竞争从而进步性能,当线程内存不足时才会加锁去共享的内存中获取内存。

接着咱们来看看 TCMalloc 的架构。

TCMalloc的架构?

TCMalloc三层逻辑架构

  • ThreadCache:线程缓存
  • CentralFreeList(CentralCache):地方缓存
  • PageHeap:堆内存

TCMalloc架构上不同的层是如何合作的?

TCMalloc把申请的内存对象按大小分为了两类:

  • 小对象 <= 256 KB
  • 大对象 > 256 KB

咱们这里以调配小对象为例,当给小对象分配内存时:

  • 先去线程缓存 ThreadCache 中调配
  • 当线程缓存 ThreadCache 的内存不足时,从对应 SizeClass 的地方缓存 CentralFreeList 获取
  • 最初,再从对应 SizeClassPageHeap中调配

Go 内存分配器的逻辑架构

采纳了和 TCMalloc 内存分配器一样的三层逻辑架构:

  • mcache:线程缓存
  • mcentral:地方缓存
  • mheap:堆内存

<p align=”center”>
<img src=”http://cdn.tigerb.cn/20220405133623.png” style=”width:60%”>
</p>

理论地方缓存 central 是一个由 136 个 mcentral 类型元素的数组形成。

除此之外须要特地留神的中央:mcache被逻辑处理器 p 持有,而并不是被真正的零碎线程 m 持有。(这个设计很有意思,后续会有一篇文章来解释这个问题)

咱们更新下架构图如下:

「Go 内存分配器」把申请的内存对象按大小分为了三类:

  • 微对象 0 < Micro Object < 16B
  • 小对象 16B =< Small Object <= 32KB
  • 大对象 32KB < Large Object

为了清晰看出这三层的关系,这里以堆上调配小对象为例:

  • 先去线程缓存 mcache 中分配内存
  • 找不到时,再去地方缓存 central 中分配内存
  • 最初间接去堆上 mheap 调配一块内存

<p align=”center”>
<img src=”http://cdn.tigerb.cn/20220405224348.png” style=”width:80%”>
</p>

架构总结

通过以上的剖析能够看出 Go 内存分配器的设计和开源 TCMalloc 内存分配器的理念、思路基本一致。比照图如下:

最初咱们总结下:

  • Go 内存分配器采纳了和 TCMalloc 一样的三层架构。逻辑上为:

    • mcache:线程缓存
    • mcentral:地方缓存
    • mheap:堆内存
  • 线程缓存 mcache 是被逻辑处理器 p 持有,而不是零碎线程m

查看《Go 语言轻松进阶》系列更多内容

链接 http://tigerb.cn/go/#/kernal/

退出移动版