Go的类型变量在内存中布局中的一个奇怪问题。

研究Go的类型变量,在内存中的布局的时候,遇到了一个怪异的问题(备注:笔者机器是64位系统,这很重要)。html

package main

import "fmt"

func main() {
	a1 := 1
	a2 := 2

	fmt.Printf("%p\n", &a1)
	fmt.Printf("%p\n", &a2)
	fmt.Println()

	b1 := "1"
	b2 := "2"

	fmt.Printf("%p\n", &b1)
	fmt.Printf("%p\n", &b2)
	fmt.Println()


	c1 := []int{1}
	c2 := []int{2}

	fmt.Printf("%p\n", &c1)
	fmt.Printf("%p\n", &c2)
}

结果git

0xc420070030
0xc420070038

0xc420070050
0xc420070060

0xc42007a000
0xc42007a020

根据结果,能够明显的看出,int类型占8个byte,string类型占16byte,因此,按照Go语言的设计官方博客说明),slice应该占24byte才对,可是事实倒是占用了32byte的空间。github

翻了不少资料,最后在雨痕的Go源码分析第四章内存分配中找到了答案。golang

分配器将其管理的内存块分为两种:架构

  • span: 由多个地址连续的页(page)组成的大块内存
  • object: 将 span 按特定大小切分红多个小块,每一个小块可存储一个对象

……函数

用于存储对象的 object,按 8 字节倍数分为 n 种。好比说,大小为 24 的 object 可用来存储 范围在 17 ~ 24 字节的对象。这种方式虽然会形成一些内存浪费,但分配器只需面对有限 的几种规格(size class)小块内存,优化了分配和复用管理策略。源码分析

由于slice须要24byte,根据Go的内存分配策略,分到32byte。布局

雨痕并无在书中指出优化

8 字节倍数分为 n 种spa

代码实如今哪里。

我在这里补充下,在$GOROOT/src/runtime/msize.go的函数initSizes找到这个实现策略。

// Initialize the size_to_class tables.
	nextsize := 0
	for sizeclass = 1; sizeclass < _NumSizeClasses; sizeclass++ {
		for ; nextsize < 1024 && nextsize <= int(class_to_size[sizeclass]); nextsize += 8 {
			size_to_class8[nextsize/8] = int8(sizeclass)
		}
		if nextsize >= 1024 {
			for ; nextsize <= int(class_to_size[sizeclass]); nextsize += 128 {
				size_to_class128[(nextsize-1024)/128] = int8(sizeclass)
			}
		}
	}

更多架构、PHP、GO相关踩坑实践技巧请关注个人公众号:PHP架构师

相关文章
相关标签/搜索