Go语言 什么逃到了堆里?

hxzsmxv2  于 2023-11-14  发布在  Go
关注(0)|答案(1)|浏览(95)

我有这样一段代码,它应该不做任何分配,但由于某种原因,它做了。
函数的哪些行进行分配,为什么?
功能:

func (vi *VarInt /* int32 */) Read(input io.Reader) error {
    var (
        b     byte
        buf   = unsafe.Slice(&b, 1)
        shift int
        value uint32
    )
    for {
        _, err := io.ReadFull(input, buf)
        if err != nil {
            return err
        }

        value |= (uint32(b) & 0b01111111) << shift

        if (b & 0b10000000) == 0 {
            *vi = VarInt(value)
            return nil
        }

        shift += 7
        if shift >= 32 {
            return ErrVarIntTooLong
        }
    }
}

func (vi *VarInt /* int32 */) Write(output io.Writer) error {
    var (
        varint [5]byte
        uvalue = uint32(*vi)
        x      int
    )
    for ; ; x++ {
        vb := uint8(uvalue)

        if (vb & 0b10000000) == 0 {
            varint[x] = vb
            break
        }

        varint[x] = (vb & 0b01111111) | 0b10000000

        uvalue >>= 7
    }

    _, err := output.Write(varint[:x+1])
    if err != nil {
        return err
    }

    return nil
}

字符串
基准:

func BenchmarkVarInt(b *testing.B) {
    var buf bytes.Buffer
    buf.Grow(5)
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        vi := (VarInt)(i)
        vi.Write(&buf)
        vi.Read(&buf)
        buf.Reset()
    }
}


我想buf切片不知何故逃脱了,但我不知道是怎么回事,因为据我所知,在这种情况下,切片是在堆栈上分配的一个结构体,它将指向变量b作为其数据。我试图将表达式unsafe.Slice(&b, 1)改为(*[1]byte)(unsafe.Pointer(&b))[:],但它什么也没改变。

gzszwxb4

gzszwxb41#

当一个值在接口中装箱时,它总是被认为是转义的-即使该值从未在调用堆栈之外使用,Go只是在这一点上停止分析,并认为有人可能已经获得了地址,因此该值必须放在堆上。
由于Read接受io.ReaderWrite接受io.Writer,因此buf(传递给两个函数的bytes.Buffer)必须转义。
即使你让这些函数接受具体的类型bytes.Buffer(你可能不想这样),这也是不够的,因为Read调用io.ReadFull,而io.ReadFull又接受io.Reader
顺便说一下,对于Read中的其他问题,有一个更简单的解决方案,不需要任何unsafe.Slice技巧:只需将var b byte替换为var b [1]byte(在内存中 * 完全相同 *),将b[:]传递给ReadFull,并在使用b的其他地方使用b[0]

相关问题