Comments (7)
看来hdr.Data = uintptr(unsafe.Pointer(&a))
这行赋值让编译器觉得将a
开在堆上比较安全。如果没有这行,a
将被开在栈上。
这个和KeepAlive
的使用应该也没有直接联系。事实上,开在栈上比开在堆上对于使用unsafe
更加得微妙。因为目前对于标准编译器来说,开在堆上的内存从来不会被移动,但是开在栈上的内存有在栈增缩的时候会被移动。开辟在两者之上的内存都有可能被回收。开在堆上内存是被垃圾回收器回收的。开在栈上的内存准确的说不叫回收,每个栈有一个游标,表示着栈顶,此游标在程序运行时,可能增加(对应开辟新的内存),或者减小(对应回收)。
一个KeepAlive
调用将使它的参数引用的内存开辟在堆上,从而避免了此内存被移动的可能。对一个值是否应该使用KeepAlive
调用的准则是:如果不使用KeepAlive
调用,此值的内存可能在此内存仍被使用之前被回收掉(或者被移动)。
from golang101.
难道说是因为len(a)操作在编译期变成了常量6
这个是肯定的,见 https://gfw.go101.org/article/summaries.html#compile-time-evaluation
不过这一点不是此问题的关键。
我重新仔细考虑了一下这个例子,感觉此处的runtime.KeepAlive
调用其实是不必要的。此例和文中其它例子中的情况还是有所区别的。因为对hdr.Data
的赋值其实就是对bs
内部的底层数组指针的赋值,所以数组a
的内存时时刻刻都被一个有效的指针引用着。
你觉得呢?
其实reflect.SliceHeader
和reflect.StringHeader
这两个类型非常不推荐使用,可以说这两个类型是一个设计失误。见 golang/go#19367
最好使用文末提到的自定义SliceHeader
和StringHeader
类型。
from golang101.
这块比较微妙,如果编译器不能保证hdr.Data = uintptr(unsafe.Pointer(&a))
被原子执行,则runtime.KeepAlive
调用还是需要的。我再到go-nuts上问问。;)
from golang101.
多谢回复。理解你在原文中表达的意思了。 赋值之后,bs
会引用着a
内存,所以不会被回收,因此关注点在赋值之前的时间。
通过命令go tool compile -N -S main.go
看了下汇编文件,只有在进行
hdr.Data = tmpPtr
才会有writeBarrier
写屏障保护,所以感觉runtime.KeepAlive(&a)
还是不应该去掉。
如果我没理解错的话,hdr.Data = uintptr(unsafe.Pointer(&a))
这一句分开两行写和写在一行并没有发现根本性的差异。
原始文件写在同一行
0x00d4 00212 (main.go:14) TESTB AL, (DI)
0x00d6 00214 (main.go:14) PCDATA $2, $4
0x00d6 00214 (main.go:14) MOVQ "".&a+168(SP), AX
0x00de 00222 (main.go:14) PCDATA $2, $-2
0x00de 00222 (main.go:14) PCDATA $0, $-2
0x00de 00222 (main.go:14) CMPL runtime.writeBarrier(SB), $0
0x00e5 00229 (main.go:14) JEQ 236
0x00e7 00231 (main.go:14) JMP 1245
0x00ec 00236 (main.go:14) MOVQ AX, "".bs+352(SP)
0x00f4 00244 (main.go:14) JMP 246
0x00f6 00246 (main.go:15) PCDATA $2, $1
0x00f6 00246 (main.go:15) PCDATA $0, $5
0x00f6 00246 (main.go:15) MOVQ "".&a+168(SP), AX
0x00fe 00254 (main.go:15) PCDATA $0, $6
0x00fe 00254 (main.go:15) MOVQ AX, ""..autotmp_28+112(SP)
0x0103 00259 (main.go:15) PCDATA $2, $0
0x0103 00259 (main.go:15) MOVQ AX, ""..autotmp_14+160(SP)
修改后的源文件
package main
import (
"fmt"
"unsafe"
"reflect"
"runtime"
)
func main() {
a := [6]byte{'G', 'o', '1', '0', '1'}
bs := []byte("Golang")
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&bs))
tmpPtr := uintptr(unsafe.Pointer(&a))
hdr.Data = tmpPtr
runtime.KeepAlive(&a) // 必不可少!
hdr.Len = 2
hdr.Cap = len(a)
fmt.Printf("%s\n", bs) // Go
bs = bs[:cap(bs)]
fmt.Printf("%s\n", bs) // Go101
}
写在不同行
0x00ba 00186 (main.go:14) PCDATA $2, $1
0x00ba 00186 (main.go:14) LEAQ "".a+82(SP), AX
0x00bf 00191 (main.go:14) PCDATA $2, $0
0x00bf 00191 (main.go:14) MOVQ AX, "".tmpPtr+88(SP)
0x00c4 00196 (main.go:15) PCDATA $2, $4
0x00c4 00196 (main.go:15) MOVQ "".hdr+120(SP), DI
0x00c9 00201 (main.go:15) TESTB AL, (DI)
0x00cb 00203 (main.go:15) PCDATA $2, $-2
0x00cb 00203 (main.go:15) PCDATA $0, $-2
0x00cb 00203 (main.go:15) CMPL runtime.writeBarrier(SB), $0
0x00d2 00210 (main.go:15) JEQ 217
0x00d4 00212 (main.go:15) JMP 1216
0x00d9 00217 (main.go:15) MOVQ AX, (DI)
0x00dc 00220 (main.go:15) JMP 222
from golang101.
感觉这个和writeBarrier的关系并不太大。writeBarrier主要是在垃圾回收执行过程对指针赋值做出的特殊处理,以防止误把仍在使用的内存标记为垃圾。
Go核心团队成员曾说过:为了避免垃圾回收算法的复杂性,目前官方编译器保证指针的赋值都是原子操作,而并未对其它赋值做出这个保证。这样垃圾回收器不会在一个指针赋值的执行撕裂成两部分,但对于其它赋值,是有可能被撕裂成两部分的,比如上面提到的hdr.Data = uintptr(unsafe.Pointer(&a))
。所以这块KeepAlive
应该还是需要的。
from golang101.
这里“垃圾回收器不会在一个指针赋值的执行撕裂成两部分”,确切地指在垃圾回收器的扫描过程不会运行在一个指针赋值的执行之间,而只能完全运行于此指针赋值的执行之前或者之后。
from golang101.
我在Windows平台下使用go build -gcflags="-m" .
对下面的源代码进行逃逸分析
package main
import (
"fmt"
"unsafe"
"reflect"
// "runtime"
)
func main() {
a := [6]byte{'G', 'o', '1', '0', '1'}
bs := []byte("Golang")
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&bs))
hdr.Data = uintptr(unsafe.Pointer(&a))
// runtime.KeepAlive(&a) // 必不可少!
hdr.Len = 2
hdr.Cap = len(a)
fmt.Printf("%s\n", bs) // Go
bs = bs[:cap(bs)]
fmt.Printf("%s\n", bs) // Go101
}
.\main.go:14:36: &a escapes to heap
.\main.go:11:2: moved to heap: a
.\main.go:18:12: io.Writer(os.Stdout) escapes to heap
.\main.go:18:13: bs escapes to heap
.\main.go:12:14: ([]byte)("Golang") escapes to heap
.\main.go:20:12: io.Writer(os.Stdout) escapes to heap
.\main.go:20:13: bs escapes to heap
.\main.go:13:47: main &bs does not escape
.\main.go:18:12: main []interface {} literal does not escape
.\main.go:20:12: main []interface {} literal does not escape
<autogenerated>:1: os.(*File).close .this does not escape
<autogenerated>:1: os.(*File).isdir .this does not escape
基于上面的讨论,此处的KeepAlive
还是加上比较合适,因为a
逃逸了,也就是在堆上分配的;如果a没有逃逸,就不会收到gc干扰,就不用加了。
多谢耐心解答。
from golang101.
Related Issues (20)
- 文章中都是长难句
- 申请使用中英文版内容
- 在线阅读网站阅读体验 HOT 1
- 原子操作CompareAndSwapT HOT 1
- 代码类型错误,应该是bool HOT 2
- 关于《Go Optimizations 101》 HOT 5
- Go Optimizations 101 提到的内联函数对性能的负面影响 HOT 2
- sync包 HOT 2
- 打印内容冗余 HOT 1
- 《Go 细节和小技巧 101》电子书下载链接 HOT 9
- 《Go 细节和小技巧 101》3.4 从字符串转换来的字节型切片的容量是未指定的 HOT 2
- 中文版的pdf在哪里下载? HOT 1
- 《如何优雅地关闭通道》中信号丢失的疑问 HOT 2
- 下载慢 HOT 7
- 希望增加《Go Optimizations 101》的国内付费购买渠道 HOT 1
- 访问《Go语言(基础知识)101》 报错 HOT 3
- 《Go细节和小技巧101》错别字与示例输出问题 HOT 2
- 代码问题 HOT 3
- 你好!关于type关键字 HOT 6
- 希望在文章的侧边增加大纲栏 HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from golang101.