转载:www.jianshu.com/p/ebd8b0125…
func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
// ...
// 最重要的两个步骤:
// 通过分配地址反查获取到 heap 的 heapBits 结构(回忆下 golang 的内存地址管理)
h := heapBitsForAddr(x)
// 获取到类型的指针 bitmap;
ptrmask := typ.gcdata // start of 1-bit pointer mask (or GC program, handled below)
var (
// ...
)
// 把 h.bitp 这个堆上的 bitmap 取出来;
hbitp = h.bitp
// 该类型的指针 bitmap
p = ptrmask
// ...
if p != nil {
// 把 bitmap 第一个字节保存起来
b = uintptr(*p)
// p 指向下一个字节
p = add1(p)
//
nb = 8
}
// 我们的是简单的 Struct 结构(48==48)
if typ.size == dataSize {
// nw == 5 == 40/8,说明扫描到第 5 个字段为止即可。
// ptrdata 指明有指针的范围在[0, 40]以内,再往外确定就没有指针字段了;
nw = typ.ptrdata / sys.PtrSize
} else {
nw = ((dataSize/typ.size-1)*typ.size + typ.ptrdata) / sys.PtrSize
}
switch {
default:
throw("heapBitsSetType: unexpected shift")
case h.shift == 0:
// b 是类型的 ptr bitmap => 00010100
// bitPointerAll => 00001111
// hb => 0000 0100
hb = b & bitPointerAll
// bitScan => 0001 0000
// 0001 0000 | 0100 0000 | 1000 0000
// hb => 1101 0100
hb |= bitScan | bitScan<<(2*heapBitsShift) | bitScan<<(3*heapBitsShift)
// 赋值 hbitp => 1101 0100
*hbitp = uint8(hb)
// 指针往后一个字节(递进一个字节)
hbitp = add1(hbitp)
// b => 0000 0001
b >>= 4
// nb => 4
nb -= 4
case sys.PtrSize == 8 && h.shift == 2:
// ...
}
// ...
// 处理完了前 4 bit,接下来处理后 4 bit
nb -= 4
for {
// b => 0000 0001
// hb => 0000 0001
hb = b & bitPointerAll
// hb => 1111 0001
hb |= bitScanAll
if w += 4; w >= nw {
// 处理完了,有指针的字段都包含在已经处理的 ptrmask 范围内了
break
}
// ...
}
Phase3:
// Phase 3: Write last byte or partial byte and zero the rest of the bitmap entries.
// 8 > 5
if w > nw {
// mask => 1
mask := uintptr(1)<<(4-(w-nw)) - 1
// hb => 0001 0001
hb &= mask | mask<<4 // apply mask to both pointer bits and scan bits
}
// nw => 6
nw = size / sys.PtrSize
// ...
if w == nw+2 {
// 赋值 hbitp => 0001 0001
*hbitp = *hbitp&^(bitPointer|bitScan|(bitPointer|bitScan)<<heapBitsShift) | uint8(hb)
}
Phase4:
// Phase 4: Copy unrolled bitmap to per-arena bitmaps, if necessary.
// ...
}
func scanstack(gp *g, gcw *gcWork) {
// ...
// 扫描栈上所有的可达的对象
state.buildIndex()
for {
p := state.getPtr()
if p == 0 {
break
}
// 获取一个到栈上对象
obj := state.findObject(p)
if obj == nil {
continue
}
// 获取到这个对象的类型
t := obj.typ
// ...
// 获取到这个类型内存块的 ptr 的 bitmap(编译期间编译器设置好)
gcdata := t.gcdata
var s *mspan
if t.kind&kindGCProg != 0 {
s = materializeGCProg(t.ptrdata, gcdata)
gcdata = (*byte)(unsafe.Pointer(s.startAddr))
}
// 扫描这个对象
// 起点:对象起始地址 => state.stack.lo + obj.off
// 终点:t.ptrdata (还记得这个吧,这个指明了指针所在内的边界)
// 指针 bitmap:t.gcdata
scanblock(state.stack.lo+uintptr(obj.off), t.ptrdata, gcdata, gcw, &state)
if s != nil {
dematerializeGCProg(s)
}
}
// ...
}
/*
b0: 扫描开始的位置
n0: 扫描结束的长度
ptrmask: 指针的 bitmap
*/
func scanblock(b0, n0 uintptr, ptrmask *uint8, gcw *gcWork, stk *stackScanState) {
b := b0
n := n0
// 扫描到长度 n 为止;
for i := uintptr(0); i < n; {
// 每个 bit 标识一个 8 字节,8个 bit (1个字节)标识 64 个字节;
// 这里计算到合适的 bits
bits := uint32(*addb(ptrmask, i/(sys.PtrSize*8)))
// 如果整个 bits == 0,那么说明这 8 个 8 字节都没有指针引用,可以直接跳到下一轮
if bits == 0 {
i += sys.PtrSize * 8
continue
}
// bits 非0,说明内部有指针引用,就必须一个个扫描查看;
for j := 0; j < 8 && i < n; j++ {
// 指针类型?只有标识了指针类型的,才有可能走到下面的逻辑去;
if bits&1 != 0 {
p := *(*uintptr)(unsafe.Pointer(b + i))
if p != 0 {
if obj, span, objIndex := findObject(p, b, i); obj != 0 {
// 如果这 8 字节指向的是可达的内存对象,那么就投入扫描队列(置灰)保护起来;
greyobject(obj, b, i, span, gcw, objIndex)
} else if stk != nil && p >= stk.stack.lo && p < stk.stack.hi {
stk.putPtr(p)
}
}
}
bits >>= 1
i += sys.PtrSize
}
}
}
/*
b : 是对象的内存地址
gcw : 是扫描队列的封装
*/
func scanobject(b uintptr, gcw *gcWork) {
// 通过对象地址 b 获取到这块内存地址对应的 hbits
hbits := heapBitsForAddr(b)
// 通过对象地址 b 获取到这块内存地址所在的 span
s := spanOfUnchecked(b)
// span 的元素大小
n := s.elemsize
if n == 0 {
throw("scanobject n == 0")
}
// ...
var i uintptr
// 每 8 个字节处理递进处理(因为堆上对象分配都是 span,每个 span 的内存块都是定长的,所以扫描边界就是 span.elemsize )
for i = 0; i < n; i += sys.PtrSize {
if i != 0 {
hbits = hbits.next()
}
// 获取到内存块的 bitmap
bits := hbits.bits()
// 确认该整个内存块没有指针,直接跳出,节约时间;
if i != 1*sys.PtrSize && bits&bitScan == 0 {
break // no more pointers in this object
}
// 确认 bits 对应的小块内存没有指针,所以可以直接到下一轮
// 如果是指针,那么就往下看看这 8 字节啥情况
if bits&bitPointer == 0 {
continue // not a pointer
}
// 把这 8 字节里面存的值取出来;
obj := *(*uintptr)(unsafe.Pointer(b + i))
// 如果 obj 有值,并且合法(不在一个 span 的内存块里)
if obj != 0 && obj-b >= n {
// 如果 obj 指向一个有效的对象,那么把这个对象置灰色,投入扫描队列,等待处理
if obj, span, objIndex := findObject(obj, b, i); obj != 0 {
greyobject(obj, b, i, span, gcw, objIndex)
}
}
}
// ...
}