go語言的動態記憶體

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

go語言動態記憶體的申請和釋放設計來自於tcmalloc
主要資料結構:
MHeap:the malloc heap,管理page
MCentral:特定類型小對象共用的free list
MCache:執行緒區域小對象的共用free list

分配小對象

  1. 尋找MCache相應大小的free list,如果free list非空,從free list中直接擷取,
    這種情況下不需要任何鎖的開銷
  2. 如果MCache的free list為空白,則從MCentral擷取一些free object
  3. 如果MCentral的free list為空白,則從MHeap申請一些page,然後將page記憶體加入到相應MCentral的free list
  4. 如果MHeap緩衝的page不足,則從作業系統申請一些page(至少1M)

分配大對象

大對象直接從MHeap從分配

申請動態記憶體

// 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 {    if size == 0 {    return unsafe.Pointer(&zerobase)    }    size0 := size     if flags&flagNoScan == 0 && typ == nil {    gothrow("malloc missing type")    }     // This function must be atomic wrt GC, but for performance reasons    // we don't acquirem/releasem on fast path. The code below does not have    // split stack checks, so it can't be preempted by GC.    // Functions like roundup/add are inlined. And onM/racemalloc are nosplit.    // If debugMalloc = true, these assumptions are checked below.    if debugMalloc {    mp := acquirem()    if mp.mallocing != 0 {    gothrow("malloc deadlock")    }    mp.mallocing = 1    if mp.curg != nil {    mp.curg.stackguard0 = ^uintptr(0xfff) | 0xbad    }    }     c := gomcache()    var s *mspan    var x unsafe.Pointer    if size <= maxSmallSize {    if flags&flagNoScan != 0 && size < maxTinySize {    // Tiny allocator.    //    // Tiny allocator combines several tiny allocation requests    // into a single memory block. The resulting memory block    // is freed when all subobjects are unreachable. The subobjects    // must be FlagNoScan (don't have pointers), this ensures that    // the amount of potentially wasted memory is bounded.    //    // Size of the memory block used for combining (maxTinySize) is tunable.    // Current setting is 16 bytes, which relates to 2x worst case memory    // wastage (when all but one subobjects are unreachable).    // 8 bytes would result in no wastage at all, but provides less    // opportunities for combining.    // 32 bytes provides more opportunities for combining,    // but can lead to 4x worst case wastage.    // The best case winning is 8x regardless of block size.    //    // Objects obtained from tiny allocator must not be freed explicitly.    // So when an object will be freed explicitly, we ensure that    // its size >= maxTinySize.    //    // SetFinalizer has a special case for objects potentially coming    // from tiny allocator, it such case it allows to set finalizers    // for an inner byte of a memory block.    //    // The main targets of tiny allocator are small strings and    // standalone escaping variables. On a json benchmark    // the allocator reduces number of allocations by ~12% and    // reduces heap size by ~20%.    tinysize := uintptr(c.tinysize)    if size <= tinysize {    tiny := unsafe.Pointer(c.tiny)    // Align tiny pointer for required (conservative) alignment.    if size&7 == 0 {    tiny = roundup(tiny, 8)    } else if size&3 == 0 {    tiny = roundup(tiny, 4)    } else if size&1 == 0 {    tiny = roundup(tiny, 2)    }    size1 := size + (uintptr(tiny) - uintptr(unsafe.Pointer(c.tiny)))    if size1 <= tinysize {    // The object fits into existing tiny block.    x = tiny    c.tiny = (*byte)(add(x, size))    c.tinysize -= uintptr(size1)    c.local_tinyallocs++    if debugMalloc {    mp := acquirem()    if mp.mallocing == 0 {    gothrow("bad malloc")    }    mp.mallocing = 0    if mp.curg != nil {    mp.curg.stackguard0 = mp.curg.stack.lo + _StackGuard    }    // Note: one releasem for the acquirem just above.    // The other for the acquirem at start of malloc.    releasem(mp)    releasem(mp)    }    return x    }    }    // Allocate a new maxTinySize block.    s = c.alloc[tinySizeClass]    v := s.freelist    if v == nil {    mp := acquirem()    mp.scalararg[0] = tinySizeClass    onM(mcacheRefill_m)    releasem(mp)    s = c.alloc[tinySizeClass]    v = s.freelist    }    s.freelist = v.next    s.ref++    //TODO: prefetch v.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 maxTinySize-size > tinysize {    c.tiny = (*byte)(add(x, size))    c.tinysize = uintptr(maxTinySize - size)    }    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])    s = c.alloc[sizeclass]    v := s.freelist    if v == nil {    mp := acquirem()    mp.scalararg[0] = uintptr(sizeclass)    onM(mcacheRefill_m)    releasem(mp)    s = c.alloc[sizeclass]    v = s.freelist    }    s.freelist = v.next    s.ref++    //TODO: prefetch    x = unsafe.Pointer(v)    if flags&flagNoZero == 0 {    v.next = nil    if size > 2*ptrSize && ((*[2]uintptr)(x))[1] != 0 {    memclr(unsafe.Pointer(v), size)    }    }    }    c.local_cachealloc += intptr(size)    } else {    mp := acquirem()    mp.scalararg[0] = uintptr(size)    mp.scalararg[1] = uintptr(flags)    onM(largeAlloc_m)    s = (*mspan)(mp.ptrarg[0])    mp.ptrarg[0] = nil    releasem(mp)    x = unsafe.Pointer(uintptr(s.start << pageShift))    size = uintptr(s.elemsize)    }     if flags&flagNoScan != 0 {    // All objects are pre-marked as noscan.    goto marked    }     // If allocating a defer+arg block, now that we've picked a malloc size    // large enough to hold everything, cut the "asked for" size down to    // just the defer header, so that the GC bitmap will record the arg block    // as containing nothing at all (as if it were unused space at the end of    // a malloc block caused by size rounding).    // The defer arg areas are scanned as part of scanstack.    if typ == deferType {    size0 = unsafe.Sizeof(_defer{})    }     // From here till marked label marking the object as allocated    // and storing type info in the GC bitmap.    {    arena_start := uintptr(unsafe.Pointer(mheap_.arena_start))    off := (uintptr(x) - arena_start) / ptrSize    xbits := (*uint8)(unsafe.Pointer(arena_start - off/wordsPerBitmapByte - 1))    shift := (off % wordsPerBitmapByte) * gcBits    if debugMalloc && ((*xbits>>shift)&(bitMask|bitPtrMask)) != bitBoundary {    println("runtime: bits =", (*xbits>>shift)&(bitMask|bitPtrMask))    gothrow("bad bits in markallocated")    }     var ti, te uintptr    var ptrmask *uint8    if size == ptrSize {    // It's one word and it has pointers, it must be a pointer.    *xbits |= (bitsPointer << 2) << shift    goto marked    }    if typ.kind&kindGCProg != 0 {    nptr := (uintptr(typ.size) + ptrSize - 1) / ptrSize    masksize := nptr    if masksize%2 != 0 {    masksize *= 2 // repeated    }    masksize = masksize * pointersPerByte / 8 // 4 bits per word    masksize++                                // unroll flag in the beginning    if masksize > maxGCMask && typ.gc[1] != 0 {    // If the mask is too large, unroll the program directly    // into the GC bitmap. It's 7 times slower than copying    // from the pre-unrolled mask, but saves 1/16 of type size    // memory for the mask.    mp := acquirem()    mp.ptrarg[0] = x    mp.ptrarg[1] = unsafe.Pointer(typ)    mp.scalararg[0] = uintptr(size)    mp.scalararg[1] = uintptr(size0)    onM(unrollgcproginplace_m)    releasem(mp)    goto marked    }    ptrmask = (*uint8)(unsafe.Pointer(uintptr(typ.gc[0])))    // Check whether the program is already unrolled.    if uintptr(atomicloadp(unsafe.Pointer(ptrmask)))&0xff == 0 {    mp := acquirem()    mp.ptrarg[0] = unsafe.Pointer(typ)    onM(unrollgcprog_m)    releasem(mp)    }    ptrmask = (*uint8)(add(unsafe.Pointer(ptrmask), 1)) // skip the unroll flag byte    } else {    ptrmask = (*uint8)(unsafe.Pointer(typ.gc[0])) // pointer to unrolled mask    }    if size == 2*ptrSize {    *xbits = *ptrmask | bitBoundary    goto marked    }    te = uintptr(typ.size) / ptrSize    // If the type occupies odd number of words, its mask is repeated.    if te%2 == 0 {    te /= 2    }    // Copy pointer bitmask into the bitmap.    for i := uintptr(0); i < size0; i += 2 * ptrSize {    v := *(*uint8)(add(unsafe.Pointer(ptrmask), ti))    ti++    if ti == te {    ti = 0    }    if i == 0 {    v |= bitBoundary    }    if i+ptrSize == size0 {    v &^= uint8(bitPtrMask << 4)    }     *xbits = v    xbits = (*byte)(add(unsafe.Pointer(xbits), ^uintptr(0)))    }    if size0%(2*ptrSize) == 0 && size0 < size {    // Mark the word after last object's word as bitsDead.    *xbits = bitsDead << 2    }    }marked:    if raceenabled {    racemalloc(x, size)    }     if debugMalloc {    mp := acquirem()    if mp.mallocing == 0 {    gothrow("bad malloc")    }    mp.mallocing = 0    if mp.curg != nil {    mp.curg.stackguard0 = mp.curg.stack.lo + _StackGuard    }    // Note: one releasem for the acquirem just above.    // The other for the acquirem at start of malloc.    releasem(mp)    releasem(mp)    }     if debug.allocfreetrace != 0 {    tracealloc(x, size, typ)    }     if rate := MemProfileRate; rate > 0 {    if size < uintptr(rate) && int32(size) < c.next_sample {    c.next_sample -= int32(size)    } else {    mp := acquirem()    profilealloc(mp, x, size)    releasem(mp)    }    }     if memstats.heap_alloc >= memstats.next_gc {    gogc(0)    }     return x}

釋放動態記憶體

go不存在類似C中的free函數,動態記憶體的釋放是由GC進行的,每次釋放不是單獨一個對象,而是一個span中n個對象

// Free n objects from a span s back into the central free list c.// Called during sweep.// Returns true if the span was returned to heap.  Sets sweepgen to// the latest generation.// If preserve=true, don't return the span to heap nor relink in MCentral lists;// caller takes care of it.boolruntime·MCentral_FreeSpan(MCentral *c, MSpan *s, int32 n, MLink *start, MLink *end, bool preserve){    bool wasempty;     if(s->incache)    runtime·throw("freespan into cached span");     // Add the objects back to s's free list.    wasempty = s->freelist == nil;    end->next = s->freelist;    s->freelist = start;    s->ref -= n;     if(preserve) {    // preserve is set only when called from MCentral_CacheSpan above,    // the span must be in the empty list.    if(s->next == nil)    runtime·throw("can't preserve unlinked span");    runtime·atomicstore(&s->sweepgen, runtime·mheap.sweepgen);    return false;    }     runtime·lock(&c->lock);     // Move to nonempty if necessary.    if(wasempty) {    runtime·MSpanList_Remove(s);    runtime·MSpanList_Insert(&c->nonempty, s);    }     // delay updating sweepgen until here.  This is the signal that    // the span may be used in an MCache, so it must come after the    // linked list operations above (actually, just after the    // lock of c above.)    runtime·atomicstore(&s->sweepgen, runtime·mheap.sweepgen);     if(s->ref != 0) {    runtime·unlock(&c->lock);    return false;    }     // s is completely freed, return it to the heap.    runtime·MSpanList_Remove(s);    s->needzero = 1;    s->freelist = nil;    runtime·unlock(&c->lock);    runtime·unmarkspan((byte*)(s->start<<PageShift), s->npages<<PageShift);    runtime·MHeap_Free(&runtime·mheap, s, 0);    return true;}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.