TCMalloc 內存分配原理簡析

一、TCMalloc

TCMalloc簡介

為啥要介紹 TCMalloc
因為golang的內存分配算法絕大部分都是來自 TCMalloc,golang只改動了其中的一小部分。所以要理解golang內存分配算法,就要先了解下TCMalloc,為後面分析golang內存做一做功課。

tcmalloc 是google開發的內存分配算法庫,最開始它是作為google的一個性能工具庫 perftools 的一部分。TCMalloc是用來替代傳統的malloc內存分配函數。它有減少內存碎片,適用於多核,更好的並行性支持等特性。
前面TC就是Thread Cache兩英文的簡寫。

它提供了很多優化,如:

  • TCMalloc用固定大小的page(頁)來執行內存獲取、分配等操作。這個特性跟Linux物理內存頁的劃分是不是有同樣的道理。
  • TCMalloc用固定大小的對象,比如8KB,16KB 等用於特定大小對象的內存分配,這對於內存獲取或釋放等操作都帶來了簡化的作用。
  • TCMalloc還利用緩存常用對象來提高獲取內存的速度。
  • TCMalloc還可以基於每個線程或者每個CPU來設置緩存大小,這是默認設置。
  • TCMalloc基於每個線程獨立設置緩存分配策略,減少了多線程之間鎖的競爭。

TCMalloc架構簡圖

來自:google tcmalloc design

  • Front-end:
    它是一個內存緩存,提供了快速分配和重分配內存給應用的功能。它主要有2部分組成:Per-thread cache 和 Per-CPU cache。

  • Middle-end:
    職責是給Front-end提供緩存。也就是說當Front-end緩存內存不夠用時,從Middle-end申請內存。它主要是 Central free list 這部分內容。

  • Back-end:
    這一塊是負責從操作系統獲取內存,並給Middle-end提供緩存使用。它主要涉及 Page Heap 內容。

TCMalloc將整個虛擬內存空間劃分為n個同等大小的Page。將n個連續的page連接在一起組成一個Span。
PageHeap向OS申請內存,申請的span可能只有一個page,也可能有n個page。

ThreadCache內存不夠用會向CentralCache申請,CentralCache內存不夠用時會向PageHeap申請,PageHeap不夠用就會向OS操作系統申請。

小對象內存分配 ThreadCache

TCMalloc 定義了很多個size class,每個size class都維護了一個可分配的的空閑列表,空閑列表中的每一項稱為一個object(如下圖),同一個size-class的空閑列表中每個object大小相同。
在申請小內存時(小於256K),TCMalloc會根據申請內存大小映射到某個size-class中。
比如,申請0到8個位元組的大小時,會被映射到size-class1中,分配8個位元組大小;申請9到16位元組大小時,會被映射到size-class2中,分配16個位元組大小….以此類推。

上面每一個object都是 N bytes。用於Thread Cache小內存分配。
這個就組成了每一個ThreadCache的free list,thread可以從各自的free list獲取對象,不需要加鎖,所以速度很快。

如果ThreadCache的free list為空呢?那就從CentralCache中的CentralFreeList中獲取若干個object到ThreadCache對應的size class列表中,然後在取出其中一個object返回。
如果CentralFreeList中的object不夠用了呢?那CentralFreeList就會向PageHeap申請一連串由Span組成頁面,並將申請的頁面切割成一系列的object之後,再將部分object轉移給ThreadCache。
如果PageHeap也不夠用了呢?那就向OS操作系統申請內存。
從上面論述可以看出,這也是一個多級緩存思想的應用。

當申請的內存大於256K時,不在通過ThreadCache分配,而是通過PageHeap直接分配大內存。

大對象內存分配 PageHeap

PageHeap負責向操作系統申請內存。
tcmalloc也是基於頁的分配方式,即每次申請至少一頁(page)的內存大小。tcmalloc中一頁大小為8KB,多數linux中一頁為4KB,tcmallo的一頁是linux一頁大小的2倍。

PageHeap申請內存時按照頁申請,但它管理分配好的page內存時的基本單位是Span,Span對象代表了連續的頁。如下圖所示:

PageHeap中是如何組織Span,如下圖

Middle end-Central Free List

CentralFreeList的作用就是從PageHeap中取出部分Span,然後按照預定大小將其拆分成固定大小的object,提供給ThreadCache使用。

[完]

二、參考