Go的Sizeof和内存对齐浅析

类型和Sizeof

Go的类型系统比较简单,从reflect包可以窥得一二:

// A Kind represents the specific kind of type that a Type represents.
// The zero Kind is not a valid kind.
type Kind uint

const (
    Invalid Kind = iota
    Bool
    Int
    Int8
    Int16
    Int32
    Int64
    Uint
    Uint8
    Uint16
    Uint32
    Uint64
    Uintptr
    Float32
    Float64
    Complex64
    Complex128
    Array
    Chan
    Func
    Interface
    Map
    Ptr
    Slice
    String
    Struct
    UnsafePointer
)

针对每一种类型,了解每种类型所占的空间对于编写以及优化Go程序有较大的帮助。以下测试均在GOARCH=amd64环境下

type Sizeof(字节数)
Bool 1
Int 8
Uint 8
Uintptr 8
Array Sizeof(type) * len
Chan 8
Func 8
Interface 16
Map 8
Ptr(Go指针) 8
Slice 8
String 16
Struct 需考虑字节对齐和填充
UnsafePointer 8

Alignment and padding

Go是一个C家族语言,Go的结构体类型基于C语言的结构体演化而来,因此关于字节对齐等概念也是通用的。通过调整结构体字段的声明顺序有时可以优化程序性能,减少内存消耗,在一些内存受限的嵌入式系统或者操作系统内核,或者你的程序达到了内存上限的场景,只要内存是有限的,该项技术就仍然有用。

关于内存对齐,在Go语言规范中可以找到如下描述:

Computer architectures may require memory addresses to be aligned; that is, for addresses of a variable to be a multiple of a factor, the variable's type's alignment. The function Alignof takes an expression denoting a variable of any type and returns the alignment of the (type of the) variable in bytes. For a variable x:
uintptr(unsafe.Pointer(&x)) % unsafe.Alignof(x) == 0

关于更详尽的Go内存布局可参考Go语言规范go101 Memory Layouts

关于以C语言结构体为基础的字节对齐和填充可参考这篇介绍详尽的文章:

关于结构体字段的字节偏移和大小可以使用下面的工具进行显示和字段调整参考:

另外,这是一个优化的小例子:

从Go1.5开始,有一点需要注意。在一个结构体结尾的一个零长度的字段(一个零长度的数组或者空结构体)要占一个字节。在padding-is-hard这篇文章可以找到详细的讨论。下面是一个简单的测试:

type EmptyEndStruct struct {
        Field bool
        _      struct{}
}
testT := EmptyEndStruct{true, struct{}{}}
fmt.Println("struct type Sizeof:", unsafe.Sizeof(testT)) // struct type Sizeof: 2

unsafe、reflect和sync/atomic

unsafe包在编译时进行计算,而reflect在运行时计算对齐的长度

  • unsafe.Alignof(t)
  • unsafe.Alignof(x.t)
  • reflect.TypeOf(t).Align()
  • reflect.TypeOf(t).FieldAlign()

另外,在sync/atomic的文档底部,详细说明了atomic包的64位原子函数由于字节对齐导致的在32位芯片上的使用限制。

Sizeof完整的测试代码

    boolTemp := true
    intTemp := 99
    var uintTemp uint = 99
    uintptrTemp := (uintptr)(unsafe.Pointer(&intTemp))
    arrayTemp := [3]int8{1, 2}
    // arrayTemp2 := [3]string{}
    chanTemp := make(<-chan string, 100)
    ll := func(a int, b string) (int, error) {
        return a + 1, errors.New("a error")
    }
    type interfaceTest interface {
        test(int, int) int
    }
    var interfaceTemp interfaceTest
    mapTemp := make(map[string]string)
    sliceTemp := []int{1, 2, 3}
    stringTemp := "hongyi"
    // Alignment and padding
    type StructTemp struct {
        Field3 bool   // 1
        Field2 int    //8
        Field4 uint64 // 8
        Field1 string //16
    }
    structTemp1 := StructTemp{true, 1, 89, "hongyi"}
    unsafePointerTemp := unsafe.Pointer(&intTemp)

    fmt.Println("bool type Sizeof:", unsafe.Sizeof(boolTemp))
    fmt.Println("int type Sizeof:", unsafe.Sizeof(intTemp))
    fmt.Println("uint type Sizeof:", unsafe.Sizeof(uintTemp))
    fmt.Println("uintptr type Sizeof:", unsafe.Sizeof(uintptrTemp))
    fmt.Println("array type Sizeof:", unsafe.Sizeof(arrayTemp))
    fmt.Println("chan type Sizeof:", unsafe.Sizeof(chanTemp))
    fmt.Println("func type Sizeof:", unsafe.Sizeof(ll))
    fmt.Println("interface type Sizeof:", unsafe.Sizeof(interfaceTemp))
    fmt.Println("map type Sizeof:", unsafe.Sizeof(mapTemp))
    if reflect.TypeOf(&intTemp).Kind() == reflect.Ptr {
        fmt.Println("ptr type Sizeof:", unsafe.Sizeof(&intTemp))
    }
    fmt.Println("slice type Sizeof:", unsafe.Sizeof(&sliceTemp))
    fmt.Println("string type Sizeof:", unsafe.Sizeof(stringTemp))
    fmt.Println("struct type Sizeof:", unsafe.Sizeof(structTemp1))
    fmt.Println("unsafePointer type Sizeof:", unsafe.Sizeof(unsafePointerTemp))

Go结构体内存对齐举例

type T1 struct {
    a int8

    // On 64-bit architectures, to make field b
    // 8-byte aligned, 7 bytes need to be padded
    // here. On 32-bit architectures, to make
    // field b 4-byte aligned, 3 bytes need to be
    // padded here.

    b int64
    c int16

    // To make the size of type T1 be a multiple
    // of the alignment guarantee of T1, on 64-bit
    // architectures, 6 bytes need to be padded
    // here, and on 32-bit architectures, 2 bytes
    // need to be padded here.
}
// The size of T1 is 24 (= 1 + 7 + 8 + 2 + 6)
// bytes on 64-bit architectures and is 16
// (= 1 + 3 + 8 + 2 + 2) on 32-bit architectures.

type T2 struct {
    a int8

    // To make field c 2-byte aligned, one byte
    // needs to be padded here on both 64-bit
    // and 32-bit architectures.

    c int16

    // On 64-bit architectures, to make field b
    // 8-byte aligned, 4 bytes need to be padded
    // here. On 32-bit architectures, field b is
    // already 4-byte aligned, so no bytes need
    // to be padded here.

    b int64
}
// The size of T2 is 16 (= 1 + 1 + 2 + 4 + 8)
// bytes on 64-bit architectures, and is 12
// (= 1 + 1 + 2 + 8) on 32-bit architectures.
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,992评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,212评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,535评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,197评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,310评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,383评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,409评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,191评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,621评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,910评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,084评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,763评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,403评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,083评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,318评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,946评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,967评论 2 351

推荐阅读更多精彩内容

  • unsafe 包简单说明 unsafe,顾名思义,是不安全的,Go定义这个包名也是这个意思,让我们尽可能的不要使用...
    Gopherzhang阅读 1,506评论 8 3
  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 13,776评论 0 38
  • 原文地址:在 Go 中恰到好处的内存对齐 问题 在开始之前,希望你计算一下 Part1 共占用的大小是多少呢? 输...
    EDDYCJY阅读 1,115评论 1 11
  • 一 我不信爱情的存在,却相信两个人在风雨同舟,共同经历或困苦或甜蜜之后所拥有的亲密感。 关于七夕最初的记忆,是大奶...
    治愈成长阅读 406评论 2 2
  • 前一段时间忽然想到同理心这个词,百度一下大概意思就是说要从对方的心理才揣度问题,而不是自我出发,以己度人。比如将心...
    墨韵书香阅读 965评论 7 0