返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

2.3.4 微小对象

发布于 2024-10-12 19:15:56 字数 4125 浏览 0 评论 0 收藏 0

微小对象(tiny)长度小于 16 字节,最常见的就是小字符串。

将多个微小对象组合起 来,用单 object 存储,可有效减少内存浪费。

// malloc.go

maxTinySize   = _TinySize
tinySizeClass = _TinySizeClass

_TinySize      = 16
_TinySizeClass = int8(2)

按 tinySizeClass 取出一块 object,存到 mcache.tiny,专用于微小对象分配。

// mcache.go

type mcache struct {
    
	// Allocator cache for tiny objects w/o pointers.
	// See "Tiny allocator" comment in malloc.go.

	// tiny points to the beginning of the current tiny block, or
	// nil if there is no current tiny block.
	//
	// tiny is a heap pointer. Since mcache is in non-GC'd memory,
	// we handle it by clearing it in releaseAll during mark
	// termination.
	//
	// tinyAllocs is the number of tiny allocations performed
	// by the P that owns this mcache.
    
	tiny       uintptr   // 内存块起始地址。
	tinyoffset uintptr   // 分配偏移位置。
	tinyAllocs uintptr   // 分配计数。
}

因垃圾回收缘故,用来组合的微小对象不能包含指针。直到单元里所有微小对象都不可达时,该内存才能回收。

通过偏移位置(tinyoffset)可判断剩余空间是否满足需求。如果可以,以此计算并返回内存地址。

不足,则提取新内存块,返回起始地址便可。最后,对比新旧两块内存,留下剩余空间更大的那块。

// malloc.go

func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
	
    c := getMCache(mp)

    var span *mspan
	var x unsafe.Pointer
	noscan := typ == nil || typ.ptrdata == 0
    
	if size <= maxSmallSize {
		if noscan && 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 noscan (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%.
            
            // 当前 tiny 偏移位置。
			off := c.tinyoffset
            
			// 对齐。
			if size&7 == 0 {
				off = alignUp(off, 8)
			} else if goarch.PtrSize == 4 && size == 12 {
				off = alignUp(off, 8)
			} else if size&3 == 0 {
				off = alignUp(off, 4)
			} else if size&1 == 0 {
				off = alignUp(off, 2)
			}
            
            // 如果剩余空间足够分配。
			if off+size <= maxTinySize && c.tiny != 0 {
                
				// 计算地址,并调整偏移位置。
				x = unsafe.Pointer(c.tiny + off)
				c.tinyoffset = off + size
				c.tinyAllocs++
                
				return x
			}
            
			// 剩余空间不足,从 mcache 获取一块新的 object。
			span = c.alloc[tinySpanClass]
			v := nextFreeFast(span)
			if v == 0 {
				v, span, shouldhelpgc = c.nextFree(tinySpanClass)
			}
            
            // 初始化这块新的 object。
			x = unsafe.Pointer(v)
			(*[2]uint64)(x)[0] = 0
			(*[2]uint64)(x)[1] = 0
            
			// 对⽐新旧两块 tiny 内存,留留下剩余空间更大的那个。
            //    c.tiny == 0,表示 old tiny 不存在。
            //    new_x.tinyoffset = size。
            //    对比两个 tinyoffset,就知道哪个剩余空间多。            
			if !raceenabled && (size < c.tinyoffset || c.tiny == 0) {
				c.tiny = uintptr(x)
				c.tinyoffset = size
			}
            
			size = maxTinySize
		} else {
            // small ...
		}
	} else {
        // large ...
	}

	return x
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文