核心API实现
mallocinit()
func mallocinit() {
initSizes()
...
// linux下最终调用mmap()来预留虚拟内存空间
// 预留的大小为_MaxMem,在linux amd_64为512GB
if ptrSize == 8 && (limit == 0 || limit > 1<<30) {
// 计算各部分占用的空间,总空间为各项之和
arenaSize := round(_MaxMem, _PageSize)
bitmapSize = arenaSize / (ptrSize * 8 / 4)
spansSize = arenaSize / _PageSize * ptrSize
spansSize = round(spansSize, _PageSize)
// 尝试从0xc000000000开始设置保留地址
// 失败,则尝试0x1c000000000~0x7fc000000000
for i := 0; i <= 0x7f; i++ {
switch {
case GOARCH == "arm64" && GOOS == "darwin":
p = uintptr(i)<<40 | uintptrMask&(0x0013<<28)
case GOARCH == "arm64":
p = uintptr(i)<<40 | uintptrMask&(0x0040<<32)
default:
p = uintptr(i)<<40 | uintptrMask&(0x00c0<<32)
}
// 之所以多分配一个_PageSize是因为mmap()分配出的可能不是按照_PageSize对其的,如果这样我们需要进行再对其
pSize = bitmapSize + spansSize + arenaSize + _PageSize
p = uintptr(sysReserve(unsafe.Pointer(p), pSize, &reserved))
if p != 0 {
break
}
}
if p == 0 {
......
}
}
// 按_PageSize对其,也是前面多分配一个_PageSize的原因
p1 := round(p, _PageSize)
// 初始化heap管理的各字段
mheap_.spans = (**mspan)(unsafe.Pointer(p1))
mheap_.bitmap = p1 + spansSize
mheap_.arena_start = p1 + (spansSize + bitmapSize)
mheap_.arena_used = mheap_.arena_start
mheap_.arena_end = p + pSize
mheap_.arena_reserved = reserved
if mheap_.arena_start&(_PageSize-1) != 0 {
......
}
// 初始化全局heap
mHeap_Init(&mheap_, spansSize)
_g_ := getg()
// 为当前线程绑定cache对象
_g_.m.mcache = allocmcache()
}
内存管理初始化在go进程被启动时调用,负责向os申请预留虚拟内存空间,并将该虚拟空间分为以下几个部分:
页所属span指针数组 || GC标记位图 || 用户内存分配区域
+---------------------+---------------+-----------------------------+
|
spans 512MB .......| bitmap 32GB | arena 512GB ..................|
+---------------------+---------------+-----------------------------+
spans 512MB: ???
bitmap 32GB: ???
arena 512GB: ???
mallocgc()
// implementation of new builtin
func newobject(typ *_type) unsafe.Pointer {
flags := uint32(0)
if typ.kind&kindNoPointers != 0 {
flags |= flagNoScan
}
return mallocgc(uintptr(typ.size), typ, flags)
}
// Allocate an object of size bytes.
// Small objects are allocated from the per-P cache's free lists.
// Large objects (> 32 kB) are allocated straight from the heap.
func mallocgc(size uintptr, typ *_type, flags uint32) unsafe.Pointer {
// Set mp.mallocing to keep from being preempted by GC.
mp := acquirem()
if mp.mallocing != 0 {
throw("malloc deadlock")
}
if mp.gsignal == getg() {
throw("malloc during signal")
}
mp.mallocing = 1
shouldhelpgc := false
dataSize := size
c := gomcache()
var s *mspan
var x unsafe.Pointer
// 小对象(<32KB)分配
if size <= maxSmallSize {
// 对极小对象(<=16B)分配的优化,见“性能优化”
if flags&flagNoScan != 0 && size < maxTinySize {
......
} else {
var sizeclass int8
if size <= 1024-8 {
sizeclass = size_to_class8[(size+7)>>3]
} else {
sizeclass = size_to_class128[(size-1024+127)>>7]
}
size = uintptr(class_to_size[sizeclass])
// 从线程本地cache的freelist分配
s = c.alloc[sizeclass]
v := s.freelist
// 如果线程本地cache的freelist为空
// 首先从全局heap中为本地线程分配一批
if v.ptr() == nil {
systemstack(func() {
mCache_Refill(c, int32(sizeclass))
})
shouldhelpgc = true
s = c.alloc[sizeclass]
v = s.freelist
}
s.freelist = v.ptr().next
s.ref++
// ?
prefetchnta(uintptr(v.ptr().next))
x = unsafe.Pointer(v)
if flags&flagNoZero == 0 {
v.ptr().next = 0
if size > 2*ptrSize && ((*[2]uintptr)(x))[1] != 0 {
memclr(unsafe.Pointer(v), size)
}
}
}
c.local_cachealloc += size
// 对大对象(>32KB)的分配
} else {
var s *mspan
shouldhelpgc = true
systemstack(func() {
s = largeAlloc(size, uint32(flags))
})
x = unsafe.Pointer(uintptr(s.start << pageShift))
size = uintptr(s.elemsize)
}
......
return x
}