Heap管理

内置运行时的编程语言通常会抛弃传统的内存分配方式,改由自主管理内存。这样可以完成类 似预分配、内存池等操作,以避开频繁地申请释放内存引起的系统调而导致的性能问题。当然,还有一个重要原因是 为了更好地配合语言的垃圾回收机制。

Go内部实现也不例外,go runtime接管了所有的内存申请和释放动作。在os上层实现了内存池机制(源自tcmalloc设计)。

Go内存池管理的核心数据结构为mHeap。该结构管理从os申请的大块内存,将大块内存切分成多种不同大小的小块,每种小块由数据结构mspan表示。mheap通过数组+链表的方式来维护所有的空闲span。

应用程序在申请内存时一般都是以object为单位。在go runtime内部必须要计算object大小,然后找到合适的mspan大小。从这里面分配出想要的内存,返回给应用程序。

程序在向go runtime申请分配某种object所需内存时,会计算出object占用的内存空间,然后找到最接近的mspan(因为mspan管理的块大小是按照固定倍数增长的方案。如一个17字节的object需要的块大小应该是24字节,存在轻微的内存浪费),将其分配出去。

对象分配

// 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
        if size <= maxSmallSize {
                // 对极小对象(<=16B)分配的优化
                if flags&flagNoScan != 0 && size < maxTinySize {
                        off := c.tinyoffset
                        // Align tiny pointer for required (conservative) alignment.
                        if size&7 == 0 {
                                off = round(off, 8)
                        } else if size&3 == 0 {
                                off = round(off, 4)
                        } else if size&1 == 0 {
                                off = round(off, 2)
                        }
                        // 将众多小对象存储在同一个block内
                        if off+size <= maxTinySize && c.tiny != nil {
                                x = add(c.tiny, off)
                                c.tinyoffset = off + size
                                c.local_tinyallocs++
                                mp.mallocing = 0
                                releasem(mp)
                                return x
                        }
                        // 存储小对象的块为nil或者之前的小对象块已经容纳不下了,申请一个新的小对象块并
                        // Allocate a new maxTinySize block.
                        s = c.alloc[tinySizeClass]
                        v := s.freelist
                        if v.ptr() == nil {
                                systemstack(func() {
                                        mCache_Refill(c, tinySizeClass)
                                })
                                shouldhelpgc = true
                                s = c.alloc[tinySizeClass]
                                v = s.freelist
                        }
                        s.freelist = v.ptr().next
                        s.ref++
                        prefetchnta(uintptr(v.ptr().next))
                        x = unsafe.Pointer(v)
                        (*[2]uint64)(x)[0] = 0
                        (*[2]uint64)(x)[1] = 0
                        // See if we need to replace the existing tiny block with the new one
                        // based on amount of remaining free space.
                        if size < c.tinyoffset {
                                c.tiny = x
                                c.tinyoffset = size
                        }
                }

Heap回收

Heap释放

results matching ""

    No results matching ""