深度探索 Go 对象模型(三)

简介: 深度探索 Go 对象模型(三)

理论验证

下面我们还是通过测试代码来验证我们的理论,我们自己定义底层的相关类型,然后通过强制类型转换,来尝试解析interface变量中的数据:

type Iface struct {
  Tab *Itab
  Data unsafe.Pointer
}
type Itab struct {
  Inter uintptr
  Type uintptr
  Hash uint32
  _ [4]byte
  Fun [1]uintptr
}
type Eface struct {
  Type uintptr
  Data unsafe.Pointer
}
func TestInterface(t *testing.T) {
  var (
    p    Point
    nilP interface{}
  )
  point := &point3d{X: 1, Y: 2, Z: 3}
  nilP = point
  fmt.Printf("eface size:%v\n", unsafe.Sizeof(nilP))
  eface := (*face.Eface)(unsafe.Pointer(&nilP))
  spew.Dump(eface.Type)
  spew.Dump(eface.Data)
  fmt.Printf("eface offset: eface._type = %v, eface.data = %v\n\n",
    unsafe.Offsetof(eface.Type), unsafe.Offsetof(eface.Data))
  p = point
  fmt.Printf("point size:%v\n", unsafe.Sizeof(p))
  iface := (*face.Iface)(unsafe.Pointer(&p))
  spew.Dump(iface.Tab)
  spew.Dump(iface.Data)
  fmt.Printf("Iface offset: iface.tab = %v, iface.data = %v\n\n",
    unsafe.Offsetof(iface.Tab), unsafe.Offsetof(iface.Data))
}

执行程序输出为:

$ go test -v -run TestInterface
=== RUN   TestInterface
eface size:16
(uintptr) 0x111f2c0
(unsafe.Pointer) 0xc00008e250
eface offset: eface._type = 0, eface.data = 8
point size:16
(*face.Itab)(0x116ec40)({
 Inter: (uintptr) 0x1122680,
 Type: (uintptr) 0x111f2c0,
 Hash: (uint32) 960374823,
 _: ([4]uint8) (len=4 cap=4) {
  00000000  00 00 00 00                                       |....|
 },
 Fun: ([1]uintptr) (len=1 cap=1) {
  (uintptr) 0x10fce20
 }
})
(unsafe.Pointer) 0xc00008e250
Iface offset: iface.tab = 0, iface.data = 8

下面我们再通过汇编代码看下,赋值操作做了什么?

type Point interface {
  Println()
}
type point3d struct {
  X, Y, Z float32
}
func (p *point3d) Println() {
  fmt.Printf("%v,%v,%v\n", p.X, p.Y, p.Z)
}
func main() {
  point := point3d{X: 1, Y: 2, Z: 3} // L18
  var (
    nilP interface{}   // L20
    p    Point        // L21
  )
  nilP = &point         // L23
  p = &point            // L24
  fmt.Println(nilP, p) 
}

通过 go tool 查看汇编代码如下:

TEXT main.main(SB) /Users/cyningsun/Documents/go/src/github.com/cyningsun/go-test/20200102-inside-golang-object-model/main/build.go
  build.go:17 0x1094de0 65488b0c2530000000      MOVQ GS:0x30, CX
  build.go:17 0x1094de9 488d4424b0              LEAQ -0x50(SP), AX
  build.go:17 0x1094dee 483b4110                CMPQ 0x10(CX), AX
  build.go:17 0x1094df2 0f86b9010000            JBE 0x1094fb1
  build.go:17 0x1094df8 4881ecd0000000          SUBQ $0xd0, SP
  build.go:17 0x1094dff 4889ac24c8000000        MOVQ BP, 0xc8(SP)
  build.go:17 0x1094e07 488dac24c8000000        LEAQ 0xc8(SP), BP
  build.go:18 0x1094e0f 488d05ea1e0200          LEAQ type.*+137216(SB), AX   // point := point3d{X: 1, Y: 2, Z: 3}
  build.go:18 0x1094e16 48890424                MOVQ AX, 0(SP)
  build.go:18 0x1094e1a e81160f7ff              CALL runtime.newobject(SB)
  build.go:18 0x1094e1f 488b442408              MOVQ 0x8(SP), AX
  build.go:18 0x1094e24 4889442458              MOVQ AX, 0x58(SP)
  build.go:18 0x1094e29 0f57c0                  XORPS X0, X0
  build.go:18 0x1094e2c f30f11442434            MOVSS X0, 0x34(SP)
  build.go:18 0x1094e32 f30f11442438            MOVSS X0, 0x38(SP)
  build.go:18 0x1094e38 f30f1144243c            MOVSS X0, 0x3c(SP)
  build.go:18 0x1094e3e f30f1005a6b80400        MOVSS $f32.3f800000(SB), X0
  build.go:18 0x1094e46 f30f11442434            MOVSS X0, 0x34(SP)
  build.go:18 0x1094e4c f30f100d9cb80400        MOVSS $f32.40000000(SB), X1
  build.go:18 0x1094e54 f30f114c2438            MOVSS X1, 0x38(SP)  
  build.go:18 0x1094e5a f30f101592b80400        MOVSS $f32.40400000(SB), X2
  build.go:18 0x1094e62 f30f1154243c            MOVSS X2, 0x3c(SP)  
  build.go:18 0x1094e68 488b442458              MOVQ 0x58(SP), AX 
  build.go:18 0x1094e6d f30f1100                MOVSS X0, 0(AX) 
  build.go:18 0x1094e71 f30f114804              MOVSS X1, 0x4(AX) 
  build.go:18 0x1094e76 f30f115008              MOVSS X2, 0x8(AX) 
  build.go:20 0x1094e7b 0f57c0                  XORPS X0, X0 // nilP interface{}  
  build.go:20 0x1094e7e 0f11442470              MOVUPS X0, 0x70(SP)// nilP 开始地址为0x70  
  build.go:21 0x1094e83 0f57c0                  XORPS X0, X0 // p Point 
  build.go:21 0x1094e86 0f11442460              MOVUPS X0, 0x60(SP) 
  build.go:23 0x1094e8b 488b442458              MOVQ 0x58(SP), AX // nilP = &point  ;0x58(SP) 为 point 的地址 
  build.go:23 0x1094e90 4889442448              MOVQ AX, 0x48(SP) // SP 指向 point 地址 
  build.go:23 0x1094e95 488d0da4860100          LEAQ type.*+98368(SB), CX // ;从内存加载 Point类型地址 到 CX 寄存器
  build.go:23 0x1094e9c 48894c2470              MOVQ CX, 0x70(SP) // ;将 Point类型地址(8字节) 保存到 0x70(即eface._type) 
  build.go:23 0x1094ea1 4889442478              MOVQ AX, 0x78(SP) // ;将 point 对象地址(8字节) 保存到 0x78(即eface.data)
  build.go:24 0x1094ea6 488b442458              MOVQ 0x58(SP), AX // p = &point 
  build.go:24 0x1094eab 4889442448              MOVQ AX, 0x48(SP) // ;SP 指向 point 地址
  build.go:24 0x1094eb0 488d0d09d50400          LEAQ go.itab.*main.point3d,main.Point(SB), CX // ;从内存加载 Point类型 itab 地址 到 CX 寄存器
  build.go:24 0x1094eb7 48894c2460              MOVQ CX, 0x60(SP) // ;将 Point类型地址(8字节) 保存到 0x70(即iface.tab) 
  build.go:24 0x1094ebc 4889442468              MOVQ AX, 0x68(SP) // ;将 point 对象地址(8字节) 保存到 0x78(即iface.data) 
  build.go:25 0x1094ec1 488b442468              MOVQ 0x68(SP), AX // fmt.Println(nilP, p) 
  ...

事实正如理论一般,在编译阶段,赋值命令被转化为类型信息和对象指针的拷贝,保存下来执行期转换所需要的一切信息。

综述

从底层代码和汇编出发,分析 struct 和 interface 的 对象模型,理清了Go 语言高级特性的底层机制。再去学习反射等表层细节,事半功倍。

参考链接:

源代码:https://github.com/cyningsun/go-test

本文作者 : cyningsun

本文地址https://www.cyningsun.com/01-12-2020/inside-the-go-object-model.html

版权声明 :本博客所有文章除特别声明外,均采用 CC BY-NC-ND 3.0 CN 许可协议。转载请注明出处!

# Golang

  1. 译|There Are No Reference Types in Go
  2. Go 语言没有引用类型,指针也与众不同
  3. 译|What “accept interfaces, return structs” means in Go
  4. 如何用好 Go interface
  5. 一个优雅的 LRU 缓存实现
目录
相关文章
|
17天前
|
Go 开发工具
百炼-千问模型通过openai接口构建assistant 等 go语言
由于阿里百炼平台通义千问大模型没有完善的go语言兼容openapi示例,并且官方答复assistant是不兼容openapi sdk的。 实际使用中发现是能够支持的,所以自己写了一个demo test示例,给大家做一个参考。
|
1月前
|
存储 安全 Java
go语言重用对象
【10月更文挑战第19天】
29 1
|
1月前
|
Go 调度 开发者
Go语言的并发编程模型
【10月更文挑战第26天】Go语言的并发编程模型
16 1
|
1月前
|
安全 测试技术 Go
Go语言中的并发编程模型解析####
在当今的软件开发领域,高效的并发处理能力是提升系统性能的关键。本文深入探讨了Go语言独特的并发编程模型——goroutines和channels,通过实例解析其工作原理、优势及最佳实践,旨在为开发者提供实用的Go语言并发编程指南。 ####
|
2月前
|
负载均衡 安全 物联网
探索Go语言的并发编程模型及其在现代应用中的优势
【10月更文挑战第10天】探索Go语言的并发编程模型及其在现代应用中的优势
|
5月前
|
缓存 编译器 Go
开发与运维线程问题之Go语言的goroutine基于线程模型实现如何解决
开发与运维线程问题之Go语言的goroutine基于线程模型实现如何解决
60 3
|
6月前
|
Go 开发者
探索Go语言的并发编程模型
通过实例详细介绍了Go语言中的并发编程模型,包括goroutine、channel的基本使用和最佳实践。深入剖析如何利用Go的并发特性提高程序性能和效率,适用于初学者和有一定经验的开发者。
|
7月前
|
安全 Go 开发者
Golang深入浅出之-Go语言中的CSP模型:深入理解并发哲学
【5月更文挑战第2天】Go语言的并发编程基于CSP模型,强调通过通信共享内存。核心概念是goroutines(轻量级线程)和channels(用于goroutines间安全数据传输)。常见问题包括数据竞争、死锁和goroutine管理。避免策略包括使用同步原语、复用channel和控制并发。示例展示了如何使用channel和`sync.WaitGroup`避免死锁。理解并发原则和正确应用CSP模型是编写高效安全并发程序的关键。
178 7
|
7月前
|
安全 Go 开发者
Golang深入浅出之-Go语言中的CSP模型:深入理解并发哲学
【5月更文挑战第1天】Go语言基于CSP理论,借助goroutines和channels实现独特的并发模型。Goroutine是轻量级线程,通过`go`关键字启动,而channels提供安全的通信机制。文章讨论了数据竞争、死锁和goroutine泄漏等问题及其避免方法,并提供了一个生产者消费者模型的代码示例。理解CSP和妥善处理并发问题对于编写高效、可靠的Go程序至关重要。
165 2
|
7月前
|
数据可视化
R语言多元(多变量)GARCH :GO-GARCH、BEKK、DCC-GARCH和CCC-GARCH模型和可视化
R语言多元(多变量)GARCH :GO-GARCH、BEKK、DCC-GARCH和CCC-GARCH模型和可视化