❶ golang gc介紹
Go語言的垃圾回收機制介紹如下:
GC定義:GC全稱為Garbage Collection,中文翻譯為垃圾回收。它是一種自動發現與釋放內存中不再使用的內存區域的過程。這些不再使用的內存區域被稱為垃圾,自動垃圾回收機制可以有效避免程序中內存泄漏的問題,提升程序性能。
垃圾回收機制類型:
GC的作用:
Go語言GC觸發條件:
Go語言GC過程:
Go語言GC演化過程:
總結:Go語言的垃圾回收機制通過不斷優化和改進,有效提升了程序性能和穩定性,減輕了開發者對內存管理的負擔,避免了內存泄漏等問題。
❷ golang是自動釋放內存嗎
golang是一門自帶垃圾回收的語言,它的內存分配器和tmalloc(thread-caching malloc)很像,大多數情況下是不需要用戶自己管理內存的。最近了解了一下golang內存管理,寫出來分享一下,不正確的地方請大佬們指出。
1.內存池:
應該有一個主要管理內存分配的部分,向系統申請大塊內存,然後進行管理和分配。
2.垃圾回收:
當分配的內存使用完之後,不直接歸還給系統,而是歸還給內存池,方便進行下一次復用。至於垃圾回收選擇標記回收,還是分代回收演算法應該符合語言設計初衷吧。
3.大小切分:
使用單獨的數組或者鏈表,把需要申請的內存大小向上取整,直接從這個數組或鏈表拿出對應的大小內存塊,方便分配內存。大的對象以頁申請內存,小的對象以塊來申請,避免內存碎片,提高內存使用率。
4.多線程管理:
每個線程應該有自己的內存塊,這樣避免同時訪問共享區的時候加鎖,提升語言的並發性,線程之間通信使用消息隊列的形式,一定不要使用共享內存的方式。提供全局性的分配鏈,如果線程內存不夠用了,可向分配鏈申請內存。
這樣的內存分配設計涵蓋了大部分語言的,上面的想法其實是把golang語言內存分配抽象出來。其實Java語言也可以以同樣的方式理解。內存池就是JVM堆,主要負責申請大塊內存;多線程管理方面是使用棧內存,每個線程有自己獨立的棧內存進行管理。
golang內存分配器
golang內存分配器主要包含三個數據結構:MHeap,MCentral以及MCache
1.MHeap:分配堆,主要是負責向系統申請大塊的內存,為下層MCentral和MCache提供內存服務。他管理的基本單位是MSpan(若干連續內存頁的數據結構)
type MSpan struct
{
MSpan *next;
MSpan *prev;
PageId start; // 開始的頁號
uintptr npages; // 頁數
…..
};
可以看出MSpan是一個雙端鏈表的形式,裡面存儲了它的一些位置信息。
通過一個基地址+(頁號*頁大小),就可以定位到這個MSpan的實際內存空間。
type MHeap struct
{
lock mutex;
free [_MaxMHeapList] mSpanList // free lists of given length
freelarge mSpanList // free lists length >= _MaxMHeapList
busy [_MaxMHeapList] mSpanList // busy lists of large objects of given length
busylarge mSpanList
};
free數組以span為序號管理多個鏈表。當central需要時,只需從free找到頁數合適的鏈表。large鏈表用於保存所有超出free和busy頁數限制的MSpan。
MHeap示意圖:
2.MCache:運行時分配池,不針對全局,而是每個線程都有自己的局部內存緩存MCache,他是實現goroutine高並發的重要因素,因為分配小對象可直接從MCache中分配,不用加鎖,提升了並發效率。
type MCache struct
{
tiny byte*; // Allocator cache for tiny objects w/o pointers.
tinysize uintptr;
alloc[NumSizeClasses] MSpan*; // spans to allocate from
};
盡可能將微小對象組合到一個tiny塊中,提高性能。
alloc[]用於分配對象,如果沒有了,則可以向對應的MCentral獲取新的Span進行操作。
線程中分配小對象(16~32K)的過程:
對於
size 介於 16 ~ 32K byte 的內存分配先計算應該分配的 sizeclass,然後去 mcache 裡面
alloc[sizeclass] 申請,如果 mcache.alloc[sizeclass] 不足以申請,則 mcache 向 mcentral
申請mcentral 給 mcache 分配完之後會判斷自己需不需要擴充,如果需要則想 mheap 申請。
每個線程內申請內存是逐級向上的,首先看MCache是否有足夠空間,沒有就像MCentral申請,再沒有就像MHeap,MHeap向系統申請內存空間。
3.MCentral:作為MHeap和MCache的承上啟下的連接。承上,從MHeap申請MSpan;啟下,將MSpan劃分為各種尺寸的對象提供給MCache使用。
type MCentral struct
{
lock mutex;
sizeClass int32;
noempty mSpanList;
empty mSpanList;
int32 nfree;
……
};
type mSpanList struct {
first *mSpan
last *mSpan
};
sizeclass: 也有成員 sizeclass,用於將MSpan進行切分。
lock: 因為會有多個 P 過來競爭。
nonempty: mspan 的雙向鏈表,當前 mcentral 中可用的 mSpan list。
empty: 已經被使用的,可以認為是一種對所有 mSpan 的 track。MCentral存在於MHeap內。
給對象 object 分配內存的主要流程:
1.object size > 32K,則使用 mheap 直接分配。
2.object size < 16 byte,使用 mcache 的小對象分配器 tiny 直接分配。 (其實 tiny 就是一個指針,暫且這么說吧。)
3.object size > 16 byte && size <=32K byte 時,先使用 mcache 中對應的 size class 分配。
4.如果 mcache 對應的 size class 的 span 已經沒有可用的塊,則向 mcentral 請求。
5.如果 mcentral 也沒有可用的塊,則向 mheap 申請,並切分。
6.如果 mheap 也沒有合適的 span,則想操作系統申請。
tcmalloc內存分配器介紹
tcmalloc(thread-caching mallo)是google推出的一種內存分配器。
具體策略:全局緩存堆和進程的私有緩存。
1.對於一些小容量的內存申請試用進程的私有緩存,私有緩存不足的時候可以再從全局緩存申請一部分作為私有緩存。
2.對於大容量的內存申請則需要從全局緩存中進行申請。而大小容量的邊界就是32k。緩存的組織方式是一個單鏈表數組,數組的每個元素是一個單鏈表,鏈表中的每個元素具有相同的大小。
golang語言中MHeap就是全局緩存堆,MCache作為線程私有緩存。
在文章開頭說過,內存池就是利用MHeap實現,大小切分則是在申請內存的時候就做了,同時MCache分配內存時,可以用MCentral去取對應的sizeClass,多線程管理方面則是通過MCache去實現。
總結:
1.MHeap是一個全局變數,負責向系統申請內存,mallocinit()函數進行初始化。如果分配內存對象大於32K直接向MHeap申請。
2.MCache線程級別管理內存池,關聯結構體P,主要是負責線程內部內存申請。
3.MCentral連接MHeap與MCache的,MCache內存不夠則向MCentral申請,MCentral不夠時向MHeap申請內存。