【博文目錄>>>】 【項目地址>>>】
Go內存統計
Go提供了運行進內存統計的方法,其實現在runtime/mstats.go文件中。go內存統計的作用有:
- Go內存實時監控
- 內存dump時,統計內存相關信息
- Go GC時,統計GC相關的信息
- 應用調試時分析內存分配等
Go中的內存統計信息結構有兩個,分別是mstats和MemStats,詳細信息如下:
mstats
// 統計信息。
// 如果您編輯此結構,請同時在下面編輯MemStats類型。它們的佈局必須完全匹配。
//
// 有關詳細說明,請參閱MemStats文檔。與MemStats不同的字段將在此處進一步記錄。
//
// 這些字段中的許多都是動態更新的,而其他字段僅在調用updatememstats時才更新。
type mstats struct {
// 一般統計。
alloc uint64 // 已分配但尚未釋放的字節
total_alloc uint64 // 分配的字節(即使已釋放)
sys uint64 // 從系統獲得的字節(應爲以下xxx_sys的總和,無鎖定,近似值)
nlookup uint64 // 指針查找數(未使用)
nmalloc uint64 // malloc的次數
nfree uint64 // 釋放的次數
// 有關malloc堆的統計信息。
// 原子更新,或者全局停頓。
//
// 與MemStats一樣,heap_sys和heap_inuse不會在手動管理的範圍內計算內存。
heap_alloc uint64 // 已分配但尚未釋放的字節(與上面的alloc相同)
heap_sys uint64 // 從系統獲得的用於GC堆的虛擬地址空間
heap_idle uint64 // 空閒跨度(span)中的字節
heap_inuse uint64 // mSpanInUse跨度(span)中的字節
heap_released uint64 // 釋放到操作系統的字節
// 運行時不直接使用heap_objects,而是由updatememstats動態計算。
heap_objects uint64 // 分配的對象總數
// 有關低級別固定大小結構分配的統計信息。
// 受FixAlloc鎖保護。
stacks_inuse uint64 // 手動管理的堆棧跨度中的字節; 自動更新或在STW期間更新
stacks_sys uint64 // 僅在mstats中計算newosproc0堆棧; 與MemStats.StackSys不同
mspan_inuse uint64 // mspan結構
mspan_sys uint64
mcache_inuse uint64 // mcache結構
mcache_sys uint64
buckhash_sys uint64 // 分析存儲桶哈希表
gc_sys uint64 // 自動更新或在STW期間更新
other_sys uint64 // 自動更新或在STW期間更新
// 有關垃圾收集器的統計信息。
//在GC期間受mheap保護或全局停機。
next_gc uint64 // 下一個GC何時結束的目標heap_live; ^0(如果禁用)
last_gc_unix uint64 // 最後一次gc(Unix時間)
pause_total_ns uint64
pause_ns [256]uint64 // 最近gc暫停長度的循環緩衝區
pause_end [256]uint64 // 最近gc結束時間的循環緩衝區(自1970年以來的納秒)
numgc uint32
numforcedgc uint32 // 用戶強制GC的數量
gc_cpu_fraction float64 // GC使用的CPU時間的一部分
enablegc bool
debuggc bool
// 有關分配大小類的統計信息。
by_size [_NumSizeClasses]struct {
size uint32
nmalloc uint64
nfree uint64
}
// 下面的統計信息不會直接導出到MemStats。
last_gc_nanotime uint64 // 最後gc(單調時間)
tinyallocs uint64 // 不會導致實際分配的微小分配的數量; 沒有直接導出到go
last_next_gc uint64 // 上一個GC:next_gc
last_heap_inuse uint64 // 前一個GC的標記終止處的heap_inuse
// triggerRatio是觸發標記的堆增長比率。
//
// 例如,如果該值爲0.6,則當活動堆達到上一個週期標記的堆大小的1.6倍時,GC應該啓動。
// 該值應≤GOGC/100,以便觸發器堆大小小於目標堆大小。這是在標記終止期間爲下一個循環的觸發設置的。
triggerRatio float64
//
// gc_trigger是觸發標記的堆大小。
//
// 當heap_live≥gc_trigger時,標記階段將開始。
// 這也是必須完成比例掃描的堆大小。
//
// 這是在標記終止期間爲下一個循環的觸發器從triggerRatio計算的。
gc_trigger uint64
// heap_live是GC認爲存活的字節數。
// 即:由最近的GC保留,此後再分配。 heap_live <= heap_alloc,
// 因爲heap_alloc包括尚未被清除的未標記對象(因此在我們分配時上升,而在掃描時下降),
// 而heap_live排除這些對象(因此僅在GC之間上升)。
//
// 這是自動更新的,沒有鎖定。爲了減少爭用,僅當從一個mcentrol獲得一個跨度(span)時才更新該爭用,
// 此時,它會計算該跨度中所有未分配的槽位(在該mcache從該mcentrol獲取另一個跨度之前將對其進行分配)。
// 因此,它稍微高估了“真實”活動堆的大小。最好高估而不是低估,因爲
// 1)觸發GC的時間比必要的要早,而不是可能太晚;
// 2)導致保守的GC速率,而不是可能太低的GC速率。
//
// 讀取應該同樣是原子的(或在STW期間)。
//
// 每當更新時,都調用traceHeapAlloc()和gcController.revise()。
heap_live uint64
// heap_scan是“可掃描”堆的字節數。這是活動堆(由heap_live計算),
// 但是省略了非掃描對象和對象的非掃描尾部。
//
// 每當更新時,都調用gcController.revise()。
heap_scan uint64
// heap_marked是前一個GC標記的字節數。標記終止後,heap_live == heap_marked,
// 但是與heap_live不同,heap_marked直到下一個標記終止才更改。
heap_marked uint64
}
MemStats
// MemStats記錄有關內存分配器的統計信息。
type MemStats struct {
// 常規統計信息。
// Alloc是分配的堆對象的字節。
//
// 這與HeapAlloc相同(請參見下文)。
Alloc uint64
// TotalAlloc是分配給堆對象的累積字節。
//
// TotalAlloc隨着分配堆對象而增加,但是與Alloc和HeapAlloc不同,釋放對象時它不會減少。
TotalAlloc uint64
// Sys是從OS獲得的內存的總字節數。
//
// Sys是以下XSys字段的總和。 Sys衡量Go運行時爲堆,棧和其他內部數據結構保留的虛擬地址空間。
// 在任何給定時刻,可能並非所有虛擬地址空間都由物理內存支持,儘管通常情況下,所有虛擬地址空間都在某個時刻。
Sys uint64
// Lookups是運行時執行的指針查找的次數。
//
// 這主要用於調試運行時內部。
Lookups uint64
// Mallocs是分配的堆對象的累積計數。
// 活動對象的數量爲Malloc-Frees。
Mallocs uint64
// Frees是已釋放的堆對象的累積計數。
Frees uint64
// 堆內存統計信息。
//
// 解釋堆統計信息需要了解Go如何組織內存。 Go將堆的虛擬地址空間劃分爲跨度(“span”),
// 即內存8K或更大的連續區域。跨度可能處於以下三種狀態之一:
//
// “空閒”跨度(span)不包含任何對象或其他數據。支持空閒範圍的物理內存可以釋放回操作系統
// (但虛擬地址空間永遠不會釋放),也可以將其轉換爲“使用中”或“堆棧”跨度(span)。
//
// 一個“使用中”的跨度至少包含一個堆對象,並且可能具有可用空間來分配更多的堆對象。
//
// “堆棧”跨度用於goroutine堆棧。堆棧跨度不視爲堆的一部分。跨度可以在堆和堆棧內存之間改變;永遠不會同時使用它們。
//
// HeapAlloc是分配的堆對象的字節。
//
// “已分配”堆對象包括所有可訪問對象,以及垃圾回收器尚未釋放的不可訪問對象。
// 具體來說,HeapAlloc隨着分配堆對象而增加,而隨着堆被清除而無法訪問的對象被釋放而減少。
// 清除過程在GC週期之間逐漸發生,因此這兩個過程同時發生,因此HeapAlloc趨於平穩變化
// (與全局停機的垃圾收集器所採用的典型鋸齒形成對比)。
HeapAlloc uint64
// HeapSys是從操作系統獲得的堆內存字節。
//
// HeapSys測量爲堆保留的虛擬地址空間量。這包括已保留但尚未使用的虛擬地址空間,
// 該虛擬地址空間不佔用物理內存,但往往很小,以及在物理內存變得不使用後已將其
// 返回給操作系統的虛擬地址空間(請參閱HeapReleased以衡量後者)。
//
// HeapSys估計堆具有的最大大小。
HeapSys uint64
// HeapIdle是空閒(未使用)跨度中的字節。
//
// 空閒跨度中沒有對象。這些範圍可以(並且可能已經)返回到操作系統,
// 或者可以重新用於堆分配,或者可以重新用作棧內存。
//
// HeapIdle減去HeapReleased估計可以返回給OS的內存量,但是該內存將由運行時保留,
// 因此它可以增長堆而無需從OS請求更多內存。如果此差異明顯大於堆大小,
// 則表明活動堆大小最近出現了短暫的峯值。
HeapIdle uint64
// HeapInuse是使用中的跨度中的字節。
//
// 使用中的跨度中至少包含一個對象。這些跨度只能用於大小大致相同的其他對象。
//
// HeapInuse減去HeapAlloc估計專用於特定大小類別的內存量,但當前未使用。這是碎片的上限,但是通常可以有效地重用此內存。
HeapInuse uint64
// HeapReleased是返回操作系統的物理內存字節。
//
// 這將從返回到OS且尚未爲堆重新獲取的span範圍中計算堆內存。
HeapReleased uint64
// HeapObjects是分配的堆對象的數量。
//
// 像HeapAlloc一樣,隨着分配對象的增加,它會增加;而隨着堆的清除和無法訪問的對象的釋放,這會減少。
HeapObjects uint64
// 棧內存統計信息。
//
// 棧不被視爲堆的一部分,但是運行時可以將堆內存的一部分重用於棧內存,反之亦然。
// StackInuse是棧跨度中的字節。
//
// 使用中的棧跨度中至少包含一個棧。這些跨度只能用於相同大小的其他棧。
//
// 沒有StackIdle,因爲未使用的棧範圍返回到堆中(因此計入HeapIdle)
StackInuse uint64
// StackSys是從OS獲得的棧內存的字節。
//
// StackSys是StackInuse,再加上直接從OS獲取的用於OS線程棧的任何內存(應該很少)。
StackSys uint64
// 堆外內存統計信息。
//
// 以下統計信息將度量未從堆內存分配的運行時內部結構(通常是因爲它們是實現堆的一部分)。
// 與堆或棧內存不同,分配給這些結構的任何內存都專用於這些結構。
//
// 這些主要用於調試運行時內存開銷。
// MSpanInuse是分配的mspan結構的字節。
MSpanInuse uint64
// MSpanSys是從操作系統獲取的用於mspan結構的內存字節。
MSpanSys uint64
// MCacheInuse是分配的mcache結構的字節。
MCacheInuse uint64
// MCacheSys是從操作系統獲取的用於mcache結構的內存字節。
MCacheSys uint64
// BuckHashSys是分析存儲桶哈希表中的內存字節。
BuckHashSys uint64
// GCSys是垃圾回收元數據中的內存字節。
GCSys uint64
// OtherSys是其他堆外運行時分配中的內存字節。
OtherSys uint64
// Garbage collector statistics.
// 垃圾收集器統計信息。
// NextGC是下一個GC週期的目標堆大小。
//
// 垃圾收集器的目標是保持HeapAlloc≤NextGC。
// 在每個GC週期結束時,根據可獲得的數據量和GOGC的值計算下一個週期的目標。
NextGC uint64
// LastGC是最後一次垃圾回收完成的時間,自1970年(UNIX時代)以來,以納秒爲單位。
LastGC uint64
// PauseTotalNs是自程序啓動以來,GC全局停機暫停的累積納秒。
//
// 在全局停機暫停期間,所有goroutine都將暫停並且只有垃圾收集器可以運行。
PauseTotalNs uint64
// PauseNs是最近GC全局停機暫停時間(以納秒爲單位)的循環緩衝區。
//
// 最近的暫停是在PauseNs[(NumGC+255)%56]處。通常,PauseNs[N%256]記錄最近的第N%256個GC週期中暫停的時間。
// 每個GC週期可能會有多個暫停;這是一個週期中所有暫停的總和。
PauseNs [256]uint64
// PauseEnd是最近的GC暫停結束時間的循環緩衝區,自1970年(UNIX時代)以來爲納秒。
//
// 此緩衝區的填充方式與PauseNs相同。每個GC週期可能會有多個暫停;這將記錄一個週期中最後一個暫停的結束。
PauseEnd [256]uint64
// NumGC是已完成的GC週期數。
NumGC uint32
// NumForcedGC是應用程序調用GC函數強制執行的GC週期數。
NumForcedGC uint32
// 自程序啓動以來,GCCPUFraction是該程序使用的該程序可用CPU時間的一部分。
//
// GCCPUFraction表示爲0到1之間的數字,其中0表示GC沒有消耗該程序的CPU。
// 自程序啓動以來,程序的可用CPU時間定義爲GOMAXPROCS的整數。也就是說,
// 如果GOMAXPROCS爲2且程序已運行10秒鐘,則其“可用CPU”爲20秒鐘。
// GCCPUFraction不包括用於寫屏障活動的CPU時間。
//
// 這與GODEBUG = gctrace = 1報告的CPU分數相同。
GCCPUFraction float64
// EnableGC表示已啓用GC。即使GOGC = off,也總是如此
EnableGC bool
// // DebugGC當前未使用。
DebugGC bool
// BySize報告按大小分類分配的統計信息。
//
// BySize [N]給出大小爲S的分配的統計信息,其中
// BySize [N-1] .Size <S≤BySize [N] .Size。
//
// 這不會報告大於BySize [60] .Size的分配。
BySize [61]struct {
// Size是此size類中對象的最大字節大小。
Size uint32
// Mallocs是在此size類中分配的堆對象的累積計數。分配的累積字節爲Size * Mallocs。
// 此大小類中的活動對象數量爲Mallocs-Frees。
Mallocs uint64
// Frees是在此size類中釋放的堆對象的累積計數。
Frees uint64
}
}
mstats和MemStats的區別有:
- mstats供內部使用,MemStats供外部使用
- mstats和MemStats的字段大部分是對應的,從by_size(BySize)開始有差別。
使用
type Garbage struct{ a int }
func notify(f *Garbage) {
stats := &runtime.MemStats{}
runtime.ReadMemStats(stats)
bytes, _ := json.MarshalIndent(stats, "", " ")
fmt.Println(string(bytes))
go ProduceFinalizedGarbage()
}
func ProduceFinalizedGarbage() {
x := &Garbage{}
runtime.SetFinalizer(x, notify)
}
func main() {
go ProduceFinalizedGarbage()
for {
runtime.GC()
time.Sleep(10 * time.Second)
}
}
完整源碼
https://github.com/Wang-Jun-Chao/go-source-read/blob/master/runtime/mstats_go.md