Source File
mem_linux.go
Belonging Package
runtime
// Copyright 2010 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.package runtimeimport ()const (_EACCES = 13_EINVAL = 22)// Don't split the stack as this method may be invoked without a valid G, which// prevents us from allocating more stack.//go:nosplitfunc ( uintptr, *sysMemStat) unsafe.Pointer {, := mmap(nil, , _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_PRIVATE, -1, 0)if != 0 {if == _EACCES {print("runtime: mmap: access denied\n")exit(2)}if == _EAGAIN {print("runtime: mmap: too much locked memory (check 'ulimit -l').\n")exit(2)}return nil}.add(int64())return}var adviseUnused = uint32(_MADV_FREE)func ( unsafe.Pointer, uintptr) {// By default, Linux's "transparent huge page" support will// merge pages into a huge page if there's even a single// present regular page, undoing the effects of madvise(adviseUnused)// below. On amd64, that means khugepaged can turn a single// 4KB page to 2MB, bloating the process's RSS by as much as// 512X. (See issue #8832 and Linux kernel bug// https://bugzilla.kernel.org/show_bug.cgi?id=93111)//// To work around this, we explicitly disable transparent huge// pages when we release pages of the heap. However, we have// to do this carefully because changing this flag tends to// split the VMA (memory mapping) containing v in to three// VMAs in order to track the different values of the// MADV_NOHUGEPAGE flag in the different regions. There's a// default limit of 65530 VMAs per address space (sysctl// vm.max_map_count), so we must be careful not to create too// many VMAs (see issue #12233).//// Since huge pages are huge, there's little use in adjusting// the MADV_NOHUGEPAGE flag on a fine granularity, so we avoid// exploding the number of VMAs by only adjusting the// MADV_NOHUGEPAGE flag on a large granularity. This still// gets most of the benefit of huge pages while keeping the// number of VMAs under control. With hugePageSize = 2MB, even// a pessimal heap can reach 128GB before running out of VMAs.if physHugePageSize != 0 {// If it's a large allocation, we want to leave huge// pages enabled. Hence, we only adjust the huge page// flag on the huge pages containing v and v+n-1, and// only if those aren't aligned.var , uintptrif uintptr()&(physHugePageSize-1) != 0 {// Compute huge page containing v.= alignDown(uintptr(), physHugePageSize)}if (uintptr()+)&(physHugePageSize-1) != 0 {// Compute huge page containing v+n-1.= alignDown(uintptr()+-1, physHugePageSize)}// Note that madvise will return EINVAL if the flag is// already set, which is quite likely. We ignore// errors.if != 0 && +physHugePageSize == {// head and tail are different but adjacent,// so do this in one call.madvise(unsafe.Pointer(), 2*physHugePageSize, _MADV_NOHUGEPAGE)} else {// Advise the huge pages containing v and v+n-1.if != 0 {madvise(unsafe.Pointer(), physHugePageSize, _MADV_NOHUGEPAGE)}if != 0 && != {madvise(unsafe.Pointer(), physHugePageSize, _MADV_NOHUGEPAGE)}}}if uintptr()&(physPageSize-1) != 0 || &(physPageSize-1) != 0 {// madvise will round this to any physical page// *covered* by this range, so an unaligned madvise// will release more memory than intended.throw("unaligned sysUnused")}var uint32if debug.madvdontneed != 0 {= _MADV_DONTNEED} else {= atomic.Load(&adviseUnused)}if := madvise(, , int32()); == _MADV_FREE && != 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(, , _MADV_DONTNEED)}}func ( unsafe.Pointer, uintptr) {// Partially undo the NOHUGEPAGE marks from sysUnused// for whole huge pages between v and v+n. This may// leave huge pages off at the end points v and v+n// even though allocations may cover these entire huge// pages. We could detect this and undo NOHUGEPAGE on// the end points as well, but it's probably not worth// the cost because when neighboring allocations are// freed sysUnused will just set NOHUGEPAGE again.sysHugePage(, )}func ( unsafe.Pointer, uintptr) {if physHugePageSize != 0 {// Round v up to a huge page boundary.:= alignUp(uintptr(), physHugePageSize)// Round v+n down to a huge page boundary.:= alignDown(uintptr()+, physHugePageSize)if < {madvise(unsafe.Pointer(), -, _MADV_HUGEPAGE)}}}// Don't split the stack as this function may be invoked without a valid G,// which prevents us from allocating more stack.//go:nosplitfunc ( unsafe.Pointer, uintptr, *sysMemStat) {.add(-int64())munmap(, )}func ( unsafe.Pointer, uintptr) {mmap(, , _PROT_NONE, _MAP_ANON|_MAP_PRIVATE|_MAP_FIXED, -1, 0)}func ( unsafe.Pointer, uintptr) unsafe.Pointer {, := mmap(, , _PROT_NONE, _MAP_ANON|_MAP_PRIVATE, -1, 0)if != 0 {return nil}return}func ( unsafe.Pointer, uintptr, *sysMemStat) {.add(int64()), := mmap(, , _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_FIXED|_MAP_PRIVATE, -1, 0)if == _ENOMEM {throw("runtime: out of memory")}if != || != 0 {throw("runtime: cannot map pages in arena address space")}}