Coder Social home page Coder Social logo

Comments (7)

TapirLiu avatar TapirLiu commented on May 18, 2024 1

看来hdr.Data = uintptr(unsafe.Pointer(&a))这行赋值让编译器觉得将a开在堆上比较安全。如果没有这行,a将被开在栈上。

这个和KeepAlive的使用应该也没有直接联系。事实上,开在栈上比开在堆上对于使用unsafe更加得微妙。因为目前对于标准编译器来说,开在堆上的内存从来不会被移动,但是开在栈上的内存有在栈增缩的时候会被移动。开辟在两者之上的内存都有可能被回收。开在堆上内存是被垃圾回收器回收的。开在栈上的内存准确的说不叫回收,每个栈有一个游标,表示着栈顶,此游标在程序运行时,可能增加(对应开辟新的内存),或者减小(对应回收)。

一个KeepAlive调用将使它的参数引用的内存开辟在堆上,从而避免了此内存被移动的可能。对一个值是否应该使用KeepAlive调用的准则是:如果不使用KeepAlive调用,此值的内存可能在此内存仍被使用之前被回收掉(或者被移动)。

from golang101.

TapirLiu avatar TapirLiu commented on May 18, 2024

难道说是因为len(a)操作在编译期变成了常量6

这个是肯定的,见 https://gfw.go101.org/article/summaries.html#compile-time-evaluation
不过这一点不是此问题的关键。

我重新仔细考虑了一下这个例子,感觉此处的runtime.KeepAlive调用其实是不必要的。此例和文中其它例子中的情况还是有所区别的。因为对hdr.Data的赋值其实就是对bs内部的底层数组指针的赋值,所以数组a的内存时时刻刻都被一个有效的指针引用着。

你觉得呢?

其实reflect.SliceHeaderreflect.StringHeader这两个类型非常不推荐使用,可以说这两个类型是一个设计失误。见 golang/go#19367
最好使用文末提到的自定义SliceHeaderStringHeader类型。

from golang101.

TapirLiu avatar TapirLiu commented on May 18, 2024

这块比较微妙,如果编译器不能保证hdr.Data = uintptr(unsafe.Pointer(&a))被原子执行,则runtime.KeepAlive调用还是需要的。我再到go-nuts上问问。;)

from golang101.

lniwn avatar lniwn commented on May 18, 2024

多谢回复。理解你在原文中表达的意思了。 赋值之后,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.

TapirLiu avatar TapirLiu commented on May 18, 2024

感觉这个和writeBarrier的关系并不太大。writeBarrier主要是在垃圾回收执行过程对指针赋值做出的特殊处理,以防止误把仍在使用的内存标记为垃圾。

Go核心团队成员曾说过:为了避免垃圾回收算法的复杂性,目前官方编译器保证指针的赋值都是原子操作,而并未对其它赋值做出这个保证。这样垃圾回收器不会在一个指针赋值的执行撕裂成两部分,但对于其它赋值,是有可能被撕裂成两部分的,比如上面提到的hdr.Data = uintptr(unsafe.Pointer(&a))。所以这块KeepAlive应该还是需要的。

from golang101.

TapirLiu avatar TapirLiu commented on May 18, 2024

这里“垃圾回收器不会在一个指针赋值的执行撕裂成两部分”,确切地指在垃圾回收器的扫描过程不会运行在一个指针赋值的执行之间,而只能完全运行于此指针赋值的执行之前或者之后。

from golang101.

lniwn avatar lniwn commented on May 18, 2024

我在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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.