1. 程式人生 > 其它 >golang的記憶體回收策略

golang的記憶體回收策略

技術標籤:go

RSS( Resident Set Size )常駐記憶體集合大小,表示相應程序在RAM中佔用了多少記憶體,並不包含在SWAP中佔用的虛擬記憶體。即使是在記憶體中的使用了共享庫的記憶體大小也一併計算在內,包含了完整的在stack和heap中的記憶體。

VSZ (Virtual Memory Size),表明是虛擬記憶體大小,表明了該程序可以訪問的所有記憶體,包括被交換的記憶體和共享庫記憶體。

MADV_DONTNEED:核心會在程序的頁表中將這些頁標記為 “未分配”,從而程序的 RSS 就會盡快釋放和變小。OS 後續可以將對應的物理頁分配給其他程序。
MADV_FREE:核心只會在頁表中將這些程序頁面標記為可回收,在需要的時候才回收這些頁面。

其中MADV_FREE是Go 1.12版本才引入的,官網上的介紹如下:


On Linux, the runtime now uses MADV_FREE to release unused memory. This is more efficient but may result in higher reported RSS. The kernel will reclaim the unused data when it is needed. To revert to the Go 1.11 behavior (MADV_DONTNEED), set the environment variable GODEBUG=
madvdontneed=1.
大意就是使用MADV_FREE方式,程式記憶體不會立刻回收,即RSS值不會立刻下降,只有當OS記憶體緊缺時才會回收Go程式的記憶體返回給OS;而Go 1.11以及之前的版本預設採用的是 MADV_DONTNEED方式,程式RSS值下降很快。因此如果需要使程式記憶體佔用下降很快的話,可設定環境變數GODEBUG=madvdontneed=1。
注:Linux 4.5及之後的版本中,預設使用MADV_FREE方式。

runtime/mem_linux.go原始碼裡註釋如下:
在這裡插入圖片描述

從1.16開始由MADV_FREE方式切換為MADV_DONTNEED方式

Go1.12 以前

Go Runtime 在 Linux 上預設使用的是 MADV_DONTNEED 策略。

  // 沒有任何奇奇怪怪的判斷
 madvise(v, n, _MADV_DONTNEED)

從整體效果來看,程序 RSS 可以下降的比較快,但從效能效率上來看差點。

#Go1.12-Go1.15

當前 Linux 核心版本 >=4.5 時,Go Runtime 在 Linux 上預設使用了效能更為高效的 MADV_FREE 策略。

 var advise uint32
 if debug.madvdontneed != 0 {
  advise = _MADV_DONTNEED
 } else {
  advise = atomic.Load(&adviseUnused)
 }
 if errno := madvise(v, n, int32(advise)); advise == _MADV_FREE && errno != 0 {
  // MADV_FREE was added in Linux 4.5. Fall back to MADV_DONTNEED if it is
  // not supported.
  atomic.Store(&adviseUnused, _MADV_DONTNEED)
  madvise(v, n, _MADV_DONTNEED)
 }

從整體效果來看,程序RSS 不會立刻下降,要等到系統有記憶體壓力了才會釋放佔用,RSS 才會下降。

https://mp.weixin.qq.com/s/l4oEJdskbWpff1E3tTNUxQ