登录
首页 >  Golang >  Go问答

在Go中使用reflect.StringHeader将字节转换为字符串会导致内存重新分配吗?

来源:stackoverflow

时间:2024-03-08 18:24:26 157浏览 收藏

在Golang实战开发的过程中,我们经常会遇到一些这样那样的问题,然后要卡好半天,等问题解决了才发现原来一些细节知识点还是没有掌握好。今天golang学习网就整理分享《在Go中使用reflect.StringHeader将字节转换为字符串会导致内存重新分配吗?》,聊聊,希望可以帮助到正在努力赚钱的你。

问题内容

我有这个小代码片段来测试将字节切片转换为字符串对象的两种方法,一个函数分配一个新的字符串对象,另一个使用不安全的指针算术来构造 string*,它不分配新的内存:

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

func bytetostring(b []byte) string {
    return string(b)
}

func bytetostringnoalloc(b []byte) string {
    if len(b) == 0 {
        return ""
    }
    sh := reflect.stringheader{uintptr(unsafe.pointer(&b[0])), len(b)}
    return *(*string)(unsafe.pointer(&sh))
}

func main() {
    b := []byte("hello")
    fmt.printf("1st element of slice: %v\n", &b[0])

    str := bytetostring(b)
    sh := (*reflect.stringheader)(unsafe.pointer(&str))
    fmt.printf("new alloc: %v\n", sh)

    tostr := bytetostringnoalloc(b)
    shnoalloc := (*reflect.stringheader)(unsafe.pointer(&tostr))
    fmt.printf("no alloc: %v\n", shnoalloc) // why different from &b[0]
}

我在 go 1.13 下运行这个程序:

1st element of slice: 0xc000076068
New alloc: &{824634204304 5}
No alloc: &{824634204264 5}

我预计“切片的第一个元素”应该打印出与“no alloc”相同的地址,但实际上它们非常不同。我哪里出错了?


解决方案


首先,类型转换调用内部函数,在本例中是 slicebytetostring。 https://golang.org/src/runtime/string.go?h=slicebytetostring#L75

它将切片的内容复制到新分配的内存中。

在第二种情况下,您将创建切片的新标头并将其转换为切片内容的新非官方持有者字符串标头。 这样做的问题是垃圾收集器不处理此类情况,并且生成的字符串标头将被标记为单个结构,该结构与保存实际内容的实际切片没有关系,因此,生成的字符串仅有效当实际内容持有者处于活动状态时(不计算此字符串标头本身)。 因此,一旦垃圾收集器扫描了实际内容,您的字符串仍将指向相同的地址但已经释放了内存,并且如果您触摸它,您将收到紧急错误或未定义的行为。

顺便说一句,不需要使用反射包及其标头,因为直接转换已经创建了新的标头:

*(*string)(unsafe.Pointer(&byte_slice))

以上就是本文的全部内容了,是否有顺利帮助你解决问题?若是能给你带来学习上的帮助,请大家多多支持golang学习网!更多关于Golang的相关知识,也可关注golang学习网公众号。

声明:本文转载于:stackoverflow 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>