前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >浅析Go内存管理架构

浅析Go内存管理架构

作者头像
用户1093396
发布2022-04-15 09:14:20
3710
发布2022-04-15 09:14:20
举报
文章被收录于专栏:TIGERB的技术博客

导读

代码语言:javascript
复制
本文基于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的内存管理器是基于Google自身开源的TCMalloc内存分配器为理念设计和实现的,关于TCMalloc内存分配器的详细介绍可以查看之前的文章《18张图解密新时代内存分配器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:堆内存

实际中央缓存central是一个由136个mcentral类型元素的数组构成。

除此之外需要特别注意的地方:mcache被逻辑处理器p持有,而并不是被真正的系统线程m持有。(这个设计很有意思,后续会有一篇文章来解释这个问题)

我们更新下架构图如下:

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

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

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

  • 先去线程缓存mcache中分配内存
  • 找不到时,再去中央缓存central中分配内存
  • 最后直接去堆上mheap分配一块内存

总结

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

最后我们总结下:

  • Go内存分配器采用了和TCMalloc一样的三层架构。逻辑上为:
    • mcache:线程缓存
    • mcentral:中央缓存
    • mheap:堆内存
  • (下篇文章内容)线程缓存mcache是被逻辑处理器p持有,而不是系统线程m
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-04-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 TIGERB的技术博客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 导读
  • 目录
    • 回顾TCMalloc内存分配器
      • Go内存分配器的逻辑架构
        • 总结
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档