上卷 程序设计
中卷 标准库
- bufio 1.18
- bytes 1.18
- io 1.18
- container 1.18
- encoding 1.18
- crypto 1.18
- hash 1.18
- index 1.18
- sort 1.18
- context 1.18
- database 1.18
- connection
- query
- queryrow
- exec
- prepare
- transaction
- scan & null
- context
- tcp
- udp
- http
- server
- handler
- client
- h2、tls
- url
- rpc
- exec
- signal
- embed 1.18
- plugin 1.18
- reflect 1.18
- runtime 1.18
- KeepAlived
- ReadMemStats
- SetFinalizer
- Stack
- sync 1.18
- atomic
- mutex
- rwmutex
- waitgroup
- cond
- once
- map
- pool
- copycheck
- nocopy
- unsafe 1.18
- fmt 1.18
- log 1.18
- math 1.18
- time 1.18
- timer
下卷 运行时
源码剖析
附录
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
unsafe 1.18
绕开安全类型的一些操作。
- 不可移植和不兼容风险。
- 内存安全风险。
获取对齐、偏移量和尺寸。
func main() { a := struct{ x byte y byte }{} println(unsafe.Alignof(a)) // 1 println(unsafe.Sizeof(a)) // 2 println(unsafe.Offsetof(a.y)) // 1 b := struct { x byte y int32 z byte }{} println(unsafe.Alignof(b)) // 4 println(unsafe.Sizeof(b)) // 12 println(unsafe.Offsetof(b.y)) // 4 }
非安全的指针转换和运算。
- 任意类型指针可转换为
Pointer
,反之亦然。 - 可在
Pointer
和uintptr
间转换。 - 和
uintptr
是个纯粹的 “整数” 不同,Pointer
可保对象存活。
func main() { d := [8]byte{} // [8]byte => int x := (*int)(unsafe.Pointer(&d)) *x = 0x1122 fmt.Printf("% X\n", d) // d[2] = 0x33 p := unsafe.Add(unsafe.Pointer(&d), unsafe.Sizeof(d[0]) * 2) *(*byte)(p) = 0x33 fmt.Printf("% X\n", d) } // 22 11 00 00 00 00 00 00 // 22 11 33 00 00 00 00 00
func main() { d := [...]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} p := &d[2] // 动态构建切片,和 s2 等价。 s := unsafe.Slice(p, 2) s2 := *(*[]byte)(unsafe.Pointer((&struct { ptr unsafe.Pointer len int cap int }{ unsafe.Pointer(p), 2, 2, }))) fmt.Printf("%T, %v\n", s, s) fmt.Printf("%T, %v\n", s2, s2) } // []uint8, [2 3] // []uint8, [2 3]
示例
越界访问。
提示:与编译后的具体内存布局有关,以下代码仅作示例,以实际为准。
package main import ( "unsafe" ) func main() { d := [3]int{1, 2, 3} x := [2]int{4, 5} // x[2] = 100 p := unsafe.Add(unsafe.Pointer(&x), unsafe.Sizeof(x[0]) * uintptr(len(x))) *(*int)(p) = 100 println(d[0]) // 100 } /* $ go build -gcflags "-N -l" $ go tool objdump -S -s "main\.main" ./test func main() { d := [3]int{1, 2, 3} 0x462c04 MOVQ $0x1, 0x20(SP) 0x462c0d MOVQ $0x2, 0x28(SP) 0x462c16 MOVQ $0x3, 0x30(SP) x := [2]int{4, 5} 0x462c25 MOVQ $0x4, 0x10(SP) 0x462c2e MOVQ $0x5, 0x18(SP) p := unsafe.Add(Pointer(&x), Sizeof(x[0]) * uintptr(len(x))) 0x462c37 LEAQ 0x20(SP), AX 0x462c3c MOVQ AX, 0x38(SP) *(*int)(p) = 100 0x462c41 MOVQ AX, 0x40(SP) 0x462c46 TESTB AL, 0(AX) 0x462c48 MOVQ $0x64, 0x20(SP) */
悬垂指针。
之所以没有 “segment fault”,是因为这段内存依旧被内存管理器缓存,并未真正释放。
package main import ( "runtime" "time" "unsafe" ) func main() { d := 123 runtime.SetFinalizer(&d, func(o *int){ println("drop!") }) // 非引用指针。 p := uintptr(unsafe.Pointer(&d)) // 两次以上,因为 Finalizer 让对象在下轮回收。 for i := 0; i < 4; i++ { time.Sleep(time.Second) runtime.GC() println("gc", i) } // 回收后访问。 println(*(*int)(unsafe.Pointer(p))) } /* gc 0 drop! gc 1 gc 2 gc 3 123 */
测试
比对 Pointer
和 uintptr
是否能保持对象存活,不被垃圾回收。
package main import ( "unsafe" "runtime" "time" ) func main() { // 容器,持有指针。 var data = make([]unsafe.Pointer, 0) // var data = make([]uintptr, 0) for i := 0; i < 10; i++ { // 10MB 数据样本。 d := [10<<20]byte{ 1, 2 } // 将样本指针添加到容器。 data = append(data, unsafe.Pointer(&d)) // data = append(data, uintptr(unsafe.Pointer(&d))) } // 强制垃圾回收。 // 不需等到调用结束,只要后续不再引用,就可能被回收。 for i := 0; i < 5; i++ { time.Sleep(time.Second) runtime.GC() } // 确保容器不被回收。 runtime.KeepAlive(&data) }
# unsafe.Pointer $ go build && GODEBUG=gctrace=1 ./test gc 1 @0.001s 26%: ..., 10->10->10 MB, 10 MB goal gc 2 @0.301s 1%: ..., 20->20->20 MB, 20 MB goal gc 3 @0.319s 2%: ..., 40->40->40 MB, 40 MB goal gc 4 @0.345s 2%: ..., 80->80->80 MB, 80 MB goal gc 5 @1.370s 0%: ..., 100->100->100 MB, 160 MB goal, ... (forced) gc 6 @2.377s 0%: ..., 100->100->100 MB, 200 MB goal, ... (forced) gc 7 @3.383s 0%: ..., 100->100->100 MB, 200 MB goal, ... (forced) gc 8 @4.391s 0%: ..., 100->100->100 MB, 200 MB goal, ... (forced)
# uintptr $ go build && GODEBUG=gctrace=1 ./test gc 1 @1.015s 0%: ..., 0->0->0 MB, 4 MB goal, ... (forced) gc 2 @2.032s 0%: ..., 0->0->0 MB, 16 MB goal, ... (forced) gc 3 @3.037s 0%: ..., 0->0->0 MB, 16 MB goal, ... (forced) gc 4 @4.046s 0%: ..., 0->0->0 MB, 16 MB goal, ... (forced)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论