golang中接口的内部实现

tech2024-07-25  47

iface 数据结构 非空接口初始化的过程就是初始化一个iface类型的结构,示例如下: //src/runtime/runtime2.go

type iface struct { tab*itab/litab 存放类型及方法指针信息 data unsafe.Pointer //数据信息 }

可以看到iface结构很简单,有两个指针类型字段。 .itab:用来存放接口自身类型和绑定的实例类型及实例相关的函数指针,具体内容后面有详细介绍。 数据指针data:指向接口绑定的实例的副本,接口的初始化也是一种值拷贝。 data指向具体的实例数据,如果传递给接口的是值类型,则data指向的是实例的副本,如果传递给接口的是指针类型,则data指向指针的副本。总而言之,无论接口的转换,还是函数调用,Go遵循一样的规则-—值传递。

//src/runtime/runtime2.go type itab struct { inter *interfacetype//接口自身的静态类型 _type *_type /l_type就是接口存放的具体实例的类型(动态类型) //hash存放具体类型的Hash值 hash uint32 // copy of _type.hash. Used for type switches. [4]byte fun [1]uintptr// variable sized. fun[0]==0 means_type does not implement inter.

itab有5个字段: .inner是指向接口类型元信息的指针。 _type是指向接口存放的具体类型元信息的指针,iface里的data指针指向的是该类型的值。一个是类型信息,另一个是类型的值。 hash是具体类型的Hash值,_type里面也有hash,这里冗余存放主要是为了接口断言或类型查询时快速访问。 .fun是一个函数指针,可以理解为C++对象模型里面的虚拟函数指针,这里虽然只有一个元素,实际上指针数组的大小是可变的,编译器负责填充,运行时使用底层指针进行访问,不会受struct类型越界检查的约束,这些指针指向的是具体类型的方法。 itab这个数据结构是非空接口实现动态调用的基础, itab的信息被编译器和链接器保存了下来,存放在可执行文件的只读存储段(.rodata)中。itab存放在静态分配的存储空间中,不受GC的限制,其内存不会被回收。

接下来介绍_type数据结构,Go语言是一种强类型的语言,编译器在编译时会做严格的类型校验。所以Go必然为每种类型维护一个类型的元信息,这个元信息在运行和反射时都会用到,Go语言的类型元信息的通用结构是_type(代码位于src/runtime/type.go),其他类型都是以_type为内嵌字段封装而成的结构体。

//src/runtime/type.go type _type struct { sizeuintptr//大小 ptrdatauintptr //size of memory prefix holding all pointers hashuint32 //类型Hash tflagtflag//类型的特征标记 alignuint8//_type作为整体变量存放时的对齐字节数 fieldalign uint8//当前结构字段的对齐字节数 uint8//基础类型枚举值和反射中的Kind一致,kind决定了如何解析该类型 kind *typeAlg//指向一个函数指针表,该表有两个函数,一个是计算类型Hash函 alg //数,另一个是比较两个类型是否相同的equal函数 //gcdata stores the Gc type data for the garbage collector. //If the KindGCProg bit is set in kind,gcdata is a cC program. //otherwise it is a ptrmask bitmap. See mbitmap.go for details. gcdata*byte//GC相关信息 nameOff//str用来表示类型名称字符串在编译后二进制文件中某个section str //的偏移量 //由链接器负责填充 ptrToThis typeoff//ptrToThis用来表示类型元信息的指针在编译后二进制文件中某个 //section的偏移量 //由链接器负责填充
最新回复(0)