Go 指针陷阱
uintptr被GC当做普通整数对象,不会阻止所“引用”的对象被回收
package main
import (
"time"
"unsafe"
)
type data struct {
x [1024 * 100]byte
}
func test() uintptr {
p := &data{}
return uintptr(unsafe.Pointer(p))
}
func main() {
const N = 10000
cache := new([N]uintptr)
for i := 0; i < N; i++ {
cache[i] = test()
time.Sleep(time.Millisecond)
}
}
root@ljolan:/home/ljolan# go build -o test gc_if.go && GODEBUG="gctrace=1" ./test
gc 1 @0.064s 0%: 0.10+0.17+0.001 ms clock, 0.40+0/0.072/0.011+0.006 ms cpu, 4->4->0 MB, 5 MB goal, 4 P
gc 2 @0.133s 0%: 0.56+0.30+0.001 ms clock, 2.2+0/0.056/0+0.006 ms cpu, 4->4->0 MB, 5 MB goal, 4 P
gc 3 @0.211s 0%: 0.014+0.067+0.001 ms clock, 0.057+0/0.051/0.020+0.005 ms cpu, 4->4->0 MB, 5 MB goal, 4 P
gc 4 @0.283s 0%: 0.041+0.75+0.008 ms clock, 0.16+0/0.27/0.12+0.035 ms cpu, 4->4->0 MB, 5 MB goal, 4 P
gc 5 @0.363s 0%: 0.11+0.099+0.001 ms clock, 0.46+0/0.063/0.005+0.006 ms cpu, 4->4->0 MB, 5 MB goal, 4 P
gc 6 @0.441s 0%: 0.014+0.93+0.002 ms clock, 0.057+0/0.063/0.066+0.011 ms cpu, 4->4->0 MB, 5 MB goal, 4 P
gc 7 @0.519s 0%: 0.052+0.17+0.001 ms clock, 0.21+0/0.066/0+0.007 ms cpu, 4->4->0 MB, 5 MB goal, 4 P
合法的unsafe.Pointer被当做普通指针,会阻止所“引用”的对象被回收
package main
import (
"time"
"unsafe"
)
type data struct {
x [1024 * 100]byte
}
func test() unsafe.Pointer {
p := &data{}
return unsafe.Pointer(p)
}
func main() {
const N = 10000
cache := new([N]unsafe.Pointer)
for i := 0; i < N; i++ {
cache[i] = test()
time.Sleep(time.Millisecond)
}
}
root@ljolan:/home/ljolan# go build -o test gc_if.go && GODEBUG="gctrace=1" ./test
gc 1 @0.070s 0%: 0.10+0.76+0.001 ms clock, 0.43+0/0.10/0.011+0.005 ms cpu, 4->4->3 MB, 5 MB goal, 4 P
gc 2 @0.137s 0%: 0.10+0.17+0.001 ms clock, 0.42+0/0.069/0.003+0.006 ms cpu, 7->7->7 MB, 8 MB goal, 4 P
gc 3 @0.277s 0%: 0.097+0.17+0.001 ms clock, 0.39+0/0.081/0.004+0.006 ms cpu, 14->14->14 MB, 15 MB goal, 4 P
gc 4 @0.554s 0%: 0.033+0.17+0.001 ms clock, 0.13+0/0.075/0.004+0.006 ms cpu, 29->29->29 MB, 30 MB goal, 4 P
gc 5 @1.098s 0%: 1.7+0.65+0.001 ms clock, 7.1+0/0.090/0+0.006 ms cpu, 56->56->56 MB, 58 MB goal, 4 P
gc 6 @2.136s 0%: 0.11+0.11+0.001 ms clock, 0.44+0/0.10/0.003+0.005 ms cpu, 111->111->111 MB, 113 MB goal, 4 P
gc 7 @4.111s 0%: 0.050+0.15+0.001 ms clock, 0.20+0/0.13/0.004+0.005 ms cpu, 216->216->216 MB, 222 MB goal, 4 P
指向对象成员的unsafe.Pointer,同样能确保对象不被回收
package main
import (
"time"
"unsafe"
)
type data struct {
x [1024 * 100]byte
y int
}
func test() unsafe.Pointer {
p := data{}
return unsafe.Pointer(&p.y)
}
func main() {
const N = 10000
cache := new([N]unsafe.Pointer)
for i := 0; i < N; i++ {
cache[i] = test()
time.Sleep(time.Millisecond)
}
}
root@ljolan:/home/ljolan# go build -o test gc_if.go && GODEBUG="gctrace=1" ./test
gc 1 @0.068s 0%: 0.037+0.13+0.001 ms clock, 0.14+0/0.095/0.022+0.007 ms cpu, 4->4->3 MB, 5 MB goal, 4 P
gc 2 @0.144s 0%: 0.051+0.14+0.001 ms clock, 0.20+0/0.051/0.031+0.006 ms cpu, 7->7->7 MB, 8 MB goal, 4 P
gc 3 @0.289s 0%: 0.13+0.22+0.003 ms clock, 0.54+0/0.10/0.087+0.013 ms cpu, 14->14->14 MB, 15 MB goal, 4 P
gc 4 @0.569s 0%: 0.25+0.26+0.006 ms clock, 1.0+0/0.20/0.047+0.024 ms cpu, 29->29->29 MB, 30 MB goal, 4 P
gc 5 @1.087s 0%: 0.075+0.15+0.001 ms clock, 0.30+0/0.083/0.005+0.006 ms cpu, 56->56->56 MB, 58 MB goal, 4 P
由于可以用 unsafe.Pointer、uintptr 创建 “dangling pointer【悬挂指针】” 等非法指针,所以在使⽤时需要特别小心。另外,cgo C.malloc 等函数所返回指针,与 GC 无关
- 指针构成的 “循环引用” 加上 runtime.SetFinalizer 会导致内存泄露
package main
import (
"fmt"
"runtime"
"time"
)
type data struct {
x [1024 * 100]byte
y *data
}
func test() {
a, b := data{}, data{}
a.y = &b
b.y = &a
runtime.SetFinalizer(&a, func(a *data) {
fmt.Printf("a %p final.\n", a)
})
runtime.SetFinalizer(&b, func(b *data) {
fmt.Printf("b %p final.\n", b)
})
}
func main() {
for {
test()
time.Sleep(time.Millisecond)
}
}
root@ljolan:/home/ljolan# go build -gcflags "-N -l" -o test2 gc_if2.go && GODEBUG="gctrace=1" ./test2
gc 1 @0.040s 0%: 0.007+0.81+0.001 ms clock, 0.031+0.20/0.69/0.41+0.005 ms cpu, 4->4->7 MB, 5 MB goal, 4 P
gc 2 @0.116s 0%: 0.028+1.2+0.001 ms clock, 0.11+0.59/1.1/1.8+0.006 ms cpu, 15->15->22 MB, 16 MB goal, 4 P
gc 3 @0.327s 0%: 0.017+3.0+0.001 ms clock, 0.071+0/2.9/7.5+0.006 ms cpu, 44->44->66 MB, 45 MB goal, 4 P
gc 4 @0.965s 0%: 0.10+8.0+0.002 ms clock, 0.40+0/7.7/22+0.009 ms cpu, 130->130->194 MB, 133 MB goal, 4 P
gc 5 @7.312s 0%: 0.68+23+0.001 ms clock, 2.7+0.59/23/66+0.006 ms cpu, 379->379->564 MB, 388 MB goal, 4 P
gc 6 @23.550s 0%: 0.048+89+0.002 ms clock, 0.19+0.56/86/213+0.010 ms cpu, 1100->1101->1639 MB, 1129 MB goal, 4 P
垃圾回收器能正确处理 “指针循环引用”,但⽆法确定 Finalizer 依赖次序,也就⽆法调用 Finalizer 函数,这会导致目标对象无法变成不可达状态,其所占⽤内存⽆法被回收。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/64653.html