閱讀 意隨行筆記 及draveness關於go的記憶體管理的部分總結
流程
使用者程式(mutator)通過分配器(allocator)從堆(heap)中獲得新記憶體空間,通過收集器(collector)回收空間分配器
go採用空閒連結串列分配器,分配記憶體,並且採用隔離適應的方法空閒連結串列分配器
當使用者程式申請記憶體時,空閒連結串列分配器會依次遍歷空閒的記憶體塊,找到足夠大的記憶體,然後申請新的資源並修改連結串列意思是說,分配的記憶體空間必然是連續的,若這塊不夠,就看下一塊夠不夠,直到找到足夠大的一塊,而不能是多塊湊起來組合的(簡而言之就是要連續記憶體)
隔離適應
將記憶體分割成多個連結串列,每個連結串列中的記憶體塊大小相同,申請記憶體時先找到滿足條件的連結串列,再從連結串列中選擇合適的記憶體塊分級
- 執行緒快取(thread cache)
- 中心快取(central cache)
- 頁堆(page heap)
執行緒快取只屬於當前執行緒,不需要加鎖所以不存在競爭
32k以上的記憶體申請需要從頁堆裡分配
記憶體管理的基本單位(mspan)
mspan實現了go記憶體管理的隔離適應效果
每個mspan能分配出去的物件大小(sizeclasses)是固定的
每個mspan裡都包含了指向上一個和下一個mspan的指標的欄位,且指向的是同一個mcache下的同sizeclasses的內容執行緒快取(mcache)
type mcache { ... alloc [numSpanClasses]*mspan //numSpanClasses = 67*2 = 134 ... }
mcache是繫結在GMP的P(排程器)上的
alloc一開始為空的大小為134(67個指標型別的和67個非指標型別的)的陣列,使用過程中再去對應規格的中心快取(mcentral)動態申請mspan中心快取(mcentral)
type mcentral struct { lock mutex spanclass spanClass //spanClass Id nonempty mSpanList // 所有未被使用的空閒span empty mSpanList // 已經被mcache拿走,未歸還的會掛載到這裡 nmalloc uint64 //這個mcentral分配mspan的累積計數 }
- 中心快取是公共區域,多個mcache都可以去申請空間所以得加鎖
- 每個中心快取只管理一種大小(spanclass)的mspan(所以mcentral的總數不超過67 * 2 = 134)
- mcache來申請mspan時,先在nonempty裡面看有沒有能用的,沒有再去empty裡面找,都沒有就去頁堆裡面重新再申請一些空間
頁堆(mheap)
每個mheap大小為64M
最多有4M個mheap
所以最多能用256TB
每個指標佔8B
所以元資訊的大小為4M*8B=32M(也就是要用32M的記憶體去記錄這256T的分配情況)
- 單個大物件的大小是否不能超過單個mheap大小(64M)
- 針對大物件歸屬的mspan,裡面管理的物件大小是不是可能不一樣???(參考第六點,是否正確?)
- mspan的個數是不是也只有134個?
- mspan裡面的元素可能來源於不同的mheap?
本作品採用《CC 協議》,轉載必須註明作者和本文連結