go中的物理内存释放

共 1915字,需浏览 4分钟

 ·

2022-02-11 13:13

madvice是go1.16版本引入的系统调用参数配置,本文解释了它的原理和作用。

go 使用 madvise 系统调用来释放物理内存给操作系统,该方法主要有两种归还类型可选:

  • MADV_DONTNEED:立即归还物理内存给操作系统,如果下次访问到该范围的内存,则会触发 page fault 异常,需要重新分配物理页,使用该类型可以减少程序的RSS占用
  • MADV_FREE:告诉操作系统这块内存已经不需要使用了,可以回收了,如果内存紧张,操作系统就会将其回收。这实际是一个lazily的释放过程。如果再次访问这块内存的时候,操作系统还没有将其回收,是不会触发 page fault 的。使用该类型,可能程序的RSS不会减少。

当前实现,首先尝试使用 MADV_FREE,如果失败了,再尝试使用 MADV_DONTNEEDMADV_FREE需要linux内核4.5及以上:

var adviseUnused = uint32(_MADV_FREE)
func sysUnused(v unsafe.Pointer, n uintptr) {
    ......
    
    var advise uint32
    // 如果设置了`GODEBUG=madvdontneed=1`,强制使用MADV_DONTNEED
    if debug.madvdontneed != 0 {
        advise = _MADV_DONTNEED
    } else {
        advise = atomic.Load(&adviseUnused)
    }
    // 首先尝试使用`MADV_FREE`
    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)
    }
}

MADV_FREE的性能会好一点,但是会让程序的RSS占用不会减少,可以通过GODEBUG=madvdontneed=1强制使用MADV_DONTNEED

go中堆内存主要使用mmap来申请的。首先,使用mmapreserve一段内存:

func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer {
 p, err := mmap(v, n, _PROT_NONE, _MAP_ANON|_MAP_PRIVATE, -10)
 if err != 0 {
  return nil
 }
 return p
}

可以看到,使用匿名映射一块内存,并且指定了 PROT_NONE ,即这块内存是不可以被访问的,因此就不会分配物理内存了。当需要真正分配内存的时候,在这块保留的内存中分配:

func sysMap(v unsafe.Pointer, n uintptr, sysStat *uint64) {
 mSysStatInc(sysStat, n) // 内存统计
 // 修改前面保留的内存的指定区域为可读写
 p, err := mmap(v, n, _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_FIXED|_MAP_PRIVATE, -10)
 if err == _ENOMEM {
  throw("runtime: out of memory")
 }
 if p != v || err != 0 {
  throw("runtime: cannot map pages in arena address space")
 }
}

当我们查看进程的内存占用时,主要关注的是RSS,而reserve的内存是不能被访问的,不会分配物理内存页,并不会影响RSS的值。

文章转载:Go开发大全

(版权归原作者所有,侵删)


点击下方“阅读原文”查看更多

浏览 143
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报