子字符串和Go垃圾回收器

7

在 Go 语言中对字符串进行子字符串操作时,不会分配新的内存空间。相反,子字符串的底层表示包含一个 Data 指针,该指针是原始字符串 Data 指针的偏移量。

这意味着,如果我有一个很大的字符串并希望跟踪一个小的子字符串,则垃圾回收器将无法释放任何大字符串,直到我释放对较短子字符串的所有引用。

Slices 也有类似的问题,但您可以通过使用 copy() 函数复制子切片来规避此问题。目前我不知道有任何类似于 strings 的复制操作。那么,如何以惯用且最快的方式制作子字符串的“副本”呢?


1
这是Go的一种缺点。应该可以使用make(string,foo[x:y])来实现此功能。 - fuz
2个回答

1

For example,

package main

import (
    "fmt"
    "unsafe"
)

type String struct {
    str *byte
    len int
}

func main() {
    str := "abc"
    substr := string([]byte(str[1:]))
    fmt.Println(str, substr)
    fmt.Println(*(*String)(unsafe.Pointer(&str)), *(*String)(unsafe.Pointer(&substr)))
}

输出:

abc bc
{0x4c0640 3} {0xc21000c940 2}

1
这个解决方案会不会导致子字符串被复制了两次?一次是转换为[]byte,另一次是转换回string? - Beevik
1
是的,不幸的是。但是字节片是临时的。 - peterSO

0

我知道这是一个老问题,但有几种方法可以在不创建两个副本的情况下完成所需数据。

首先是创建子字符串的[]byte,然后使用unsafe.Pointer将其强制转换为string。这是有效的,因为[]byte的头与string的头相同,只是[]byte在末尾有一个额外的Cap字段,因此它会被截断。

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    str := "foobar"
    byt := []byte(str[3:])
    sub := *(*string)(unsafe.Pointer(&byt))
    fmt.Println(str, sub)
}

第二种方法是使用reflect.StringHeaderreflect.SliceHeader进行更明确的头部传输。
package main

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

func main() {
    str := "foobar"
    byt := []byte(str[3:])
    bytPtr := (*reflect.SliceHeader)(unsafe.Pointer(&byt)).Data
    strHdr := reflect.StringHeader{Data: bytPtr, Len: len(byt)}
    sub := *(*string)(unsafe.Pointer(&strHdr))
    fmt.Println(str, sub)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接