Golang中空结构体的地址如何分配,先看看测试结果:
package main
import "fmt"
func main() {
Test()
}
func Test() {
e1 := Empty{}
e2 := Empty{}
e3 := Empty{}
fmt.Printf("e1 : %p\ne2 : %p\ne3 : %p", &e1, &e2, &e3)
}
type Empty struct {
}
输出结果:
PS E:\develop\gotest> go run .\main.go
e1 : 0x3cc578
e2 : 0x3cc578
e3 : 0x3cc578
PS E:\develop\gotest>
查看汇编信息:
0x0036 00054 (.\main.go:10) LEAQ runtime.zerobase(SB), AX
0x003d 00061 (.\main.go:10) MOVQ AX, "".&e1+112(SP)
0x0042 00066 (.\main.go:11) LEAQ runtime.zerobase(SB), AX
0x0049 00073 (.\main.go:11) MOVQ AX, "".&e2+104(SP)
0x004e 00078 (.\main.go:12) LEAQ runtime.zerobase(SB), AX
0x0055 00085 (.\main.go:12) MOVQ AX, "".&e3+96(SP)
看到在第10行、第11行、第12行都调用了 runtime.zerobase(SB)
翻看源码,在runtime/malloc.go文件中找到zerobase变量的声明,注释写明“所有0字节分配的基地址”:
// base address for all 0-byte allocations
var zerobase uintptr
如果换成new一个对象,看看是什么样子:
func Test() {
e1 := new(Empty)
e2 := new(Empty)
e3 := new(Empty)
fmt.Printf("e1 : %p\ne2 : %p\ne3 : %p", &e1, &e2, &e3)
}
0x0036 00054 (.\main.go:10) LEAQ type.*"".Empty(SB), AX
0x003d 00061 (.\main.go:10) MOVQ AX, (SP)
0x0041 00065 (.\main.go:10) PCDATA $1, $0
0x0041 00065 (.\main.go:10) CALL runtime.newobject(SB)
0x0046 00070 (.\main.go:10) MOVQ 8(SP), DI
0x004b 00075 (.\main.go:10) MOVQ DI, "".&e1+112(SP)
0x0050 00080 (.\main.go:10) PCDATA $0, $-2
0x0050 00080 (.\main.go:10) CMPL runtime.writeBarrier(SB), $0
0x0057 00087 (.\main.go:10) JEQ 94
0x0059 00089 (.\main.go:10) JMP 534
0x005e 00094 (.\main.go:10) LEAQ runtime.zerobase(SB), AX
0x0065 00101 (.\main.go:10) MOVQ AX, (DI)
0x0068 00104 (.\main.go:10) JMP 106
0x006a 00106 (.\main.go:11) PCDATA $0, $-1
0x006a 00106 (.\main.go:11) LEAQ type.*"".Empty(SB), AX
0x0071 00113 (.\main.go:11) MOVQ AX, (SP)
0x0075 00117 (.\main.go:11) PCDATA $1, $1
0x0075 00117 (.\main.go:11) CALL runtime.newobject(SB)
0x007a 00122 (.\main.go:11) MOVQ 8(SP), DI
0x007f 00127 (.\main.go:11) MOVQ DI, "".&e2+104(SP)
0x0084 00132 (.\main.go:11) PCDATA $0, $-2
0x0084 00132 (.\main.go:11) CMPL runtime.writeBarrier(SB), $0
0x008b 00139 (.\main.go:11) JEQ 146
0x008d 00141 (.\main.go:11) JMP 517
0x0092 00146 (.\main.go:11) LEAQ runtime.zerobase(SB), AX
0x0099 00153 (.\main.go:11) MOVQ AX, (DI)
0x009c 00156 (.\main.go:11) JMP 158
0x009e 00158 (.\main.go:12) PCDATA $0, $-1
0x009e 00158 (.\main.go:12) LEAQ type.*"".Empty(SB), AX
0x00a5 00165 (.\main.go:12) MOVQ AX, (SP)
0x00a9 00169 (.\main.go:12) PCDATA $1, $2
0x00a9 00169 (.\main.go:12) CALL runtime.newobject(SB)
0x00ae 00174 (.\main.go:12) MOVQ 8(SP), DI
0x00b3 00179 (.\main.go:12) MOVQ DI, "".&e3+96(SP)
0x00b8 00184 (.\main.go:12) PCDATA $0, $-2
0x00b8 00184 (.\main.go:12) CMPL runtime.writeBarrier(SB), $0
0x00bf 00191 (.\main.go:12) NOP
0x00c0 00192 (.\main.go:12) JEQ 199
0x00c2 00194 (.\main.go:12) JMP 495
0x00c7 00199 (.\main.go:12) LEAQ runtime.zerobase(SB), AX
0x00ce 00206 (.\main.go:12) MOVQ AX, (DI)
0x00d1 00209 (.\main.go:12) JMP 211
注意这里的代码:
// implementation of new builtin
// compiler (both frontend and SSA backend) knows the signature
// of this function
func newobject(typ *_type) unsafe.Pointer {
return mallocgc(typ.size, typ, true)
}
// Allocate an object of size bytes.
// Small objects are allocated from the per-P cache's free lists.
// Large objects (> 32 kB) are allocated straight from the heap.
func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
if gcphase == _GCmarktermination {
throw("mallocgc called with gcphase == _GCmarktermination")
}
if size == 0 {
return unsafe.Pointer(&zerobase)
}
...
}
这里发现,当size等于0的时候,同样返回 zerobase。
所以在同一个进程中,空结构体创建的对象,指向同一地址,zerobase。