文档章节

golang 内存分析之字节对齐规则

鼎铭
 鼎铭
发布于 2018/06/12 22:40
字数 913
阅读 246
收藏 1

    c 的字节对齐很重要,因为c 支持指针运算。在golang 里面一般是慎用指针运算的,所以,这部分很少用,但是有些场景为了性能不得不用到指针运算,这个时候,知道golang 的内存分配就很重要了。但是基本很少有相关的参考资料,很多也不靠谱,这里借鉴c 的规则验证golang 的内存对齐规则。

该文章后续仍在不断的更新修改中, 请移步到原文地址http://www.dmwan.cc/?p=154

    首先,有个问题,为什么 函数 unsafe.Offsetof(A.a1) 的参数怪怪的,非得把结构体类型也传进去?

    c中字节对齐规则:
    1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的地址必须是它本身大小或对齐参数两者中较小的一个的倍数。
    2、整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,整体长度必须是对齐参数和结构体最长的元素长度中较小的一个的倍数。

    参考规则我们看下面这些结构的数据分配,注:64位平台,对齐参数8.

package main
import (
"fmt"
"unsafe"
)
type A struct {
	a1 int8     // offset = 0
	a2 int16    // offset = 1 / min(8, sizeof(int16)=2 )=2
}
// 4 / min(8, 2) 大小为4

type B struct {
	b1 int16   // offset = 0
	b2 int8    // offset = 2 / min(8, 1) = 2
	b3 int	   // offset = 3 / min(8, 1) = 3
}
// [0, 1] + 2 + [3, 10]=11 / min(8, 8) 不尽,不符合规则2,大小为16,内存圆整

type C struct {
    c1 int8    // offset = 0
    c2 float32 // offset = 1 /min(8, 4) = 4
    c3 int     // offset = 8 / min(8, 8)=8
}
// 16/ min(8, 8)  大小为16

type D struct {
	d1 float32 // offset = 0
	d2 int     // offset = 4 / min(8, 8) = 8
	d3 int8    // offset = 16 / min(8, 1) = 16
}

// 17 /min(8, 8), 大小为17,数据圆整,Padding 为24 /8 = 3能整除


func main() {
	var a = A{}
	var b = B{}
	var c = C{}
	var d = D{}
	// size of A = 4
	// a1: 1 字节 + 1 字节padding
	// a2: 2 字节
    fmt.Printf("a.a1 offset %v \n", unsafe.Offsetof(a.a1))
    fmt.Printf("a.a2 offset %v \n", unsafe.Offsetof(a.a2))
	fmt.Printf("size of A = %d\n", unsafe.Sizeof(a))
	// size of B = 16
	// b1: 2 字节
	// b2: 1 字节 + 5 字节padding
	// b3: 8 字节
    fmt.Printf("b.b1 offset %v \n", unsafe.Offsetof(b.b1))
    fmt.Printf("b.b2 offset %v \n", unsafe.Offsetof(b.b2))
    fmt.Printf("b.b3 offset %v \n", unsafe.Offsetof(b.b3))
	fmt.Printf("size of B = %d\n", unsafe.Sizeof(b))
	// size of c = 16
	// c1: 1 字节 + 3 字节padding
	// c2: 4 字节
	// c3: 8 字节
    fmt.Printf("c.c1 offset %v \n", unsafe.Offsetof(c.c1))
    fmt.Printf("c.c2 offset %v \n", unsafe.Offsetof(c.c2))
    fmt.Printf("c.c3 offset %v \n", unsafe.Offsetof(c.c3))
	fmt.Printf("size of C = %d\n", unsafe.Sizeof(c))
	// size of d = 24
	// d1: 4字节 + 4字节padding
	// d2: 8 字节
	// d3: 1字节 + 7 字节padding
	// d1的尾部padding的原因是要保证是结构体自身也是对齐的
	// 因为这样可以确保实现结构体数组时候里面每个元素也是对齐的
    fmt.Printf("d.d1 offset %v \n", unsafe.Offsetof(d.d1))
    fmt.Printf("d.d2 offset %v \n", unsafe.Offsetof(d.d2))
    fmt.Printf("d.d3 offset %v \n", unsafe.Offsetof(d.d3))
	fmt.Printf("size of D = %d\n", unsafe.Sizeof(d))
	// 由于有补齐,两个结构体即便有相同类型的字段,但前后顺序不同也可导致size不同
}

    发现c 的内存分配规则其实和golang 是一致的。最后其实这里是不是一致对于使用来说不会有太大影响,因为指针计算,转换之前,会提前用offsetof 计算,这个偏移,golang 会将padding 都算进去, 和c 用的时候不太一样,这样做,不会出错。

    这里特别提下就是不同平台和对齐参数的不一致,会导致结果完全不同。

 

© 著作权归作者所有

共有 人打赏支持
鼎铭
粉丝 49
博文 70
码字总数 44459
作品 0
东城
程序员
私信 提问
golang: 利用unsafe操作未导出变量

看了 @喻恒春 大神的利用unsafe.Pointer来突破私有成员,觉得例子举得不太好。而且不应该简单的放个demo,至少要讲一下其中的原理,让看的童鞋明白所以然。see:http://my.oschina.net/achun...

陈亦
2014/01/17
0
10
Go语言之 unsafe 包之内存布局

unsafe,顾名思义,是不安全的,Go定义这个包名也是这个意思,让我们尽可能的不要使用它,如果你使用它,看到了这个名字,也会想到尽可能的不要使用它,或者更小心的使用它。 虽然这个包不安...

baby神
2018/07/04
0
0
联合体、结构体简析

1. 联合体、结构体定义 联合体:在进行某些算法的C语言编程的时候,需要使几种不同类型的变量存放到同一段内存单元中。也就是使用覆盖技术,几个变量互相覆盖。这种几个不同的变量共同占用一...

PurpleXuan
2013/04/05
0
0
Java 内存空间占用规则分析

一个对象实例占用了多少字节,消耗了多少内存?这样的问题在c或c++里使用sizeof()方法就可以得到明确答案,在java里好像没有这样的方法(java一样可以实现),不过通过jmap工具倒是可以查看出...

年少爱追梦
2016/08/12
26
0
c内存对齐--影响php变量占用的内存

当在C中定义了一个结构类型时,它的大小是否等于各字段(field)大小之和?编译器将如何在内存中放置这些字段?ANSI C对结构体的内存布局有什么要求?而我们的程序又能否依赖这种布局?这些问题...

clearchen
2012/08/05
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Httpd 整合 Tomcat 步骤

环境:Tomcat8 + Httpd2.4 工作原理:借助于Tomcat的AJP连接器实现Apache与Tomcat的通信 配置步骤: 1. 配置httpd.conf 新增: Include conf/extra/mod_jk.conf 修改:添加 index.jsp <IfM...

ZeroneLove
昨天
1
0
Docker笔记3——容器命令(未写完,明天整理接着写)

未写完,明天整理接着写 新建并启动容器 docker run docker run [OPTIONS] IMAGE [COMMEND] [ARG...] OPTIONS: --name=[容器新名字] :为容器指定一个名称 -d:后台运行容器,并返回容器ID,...

HappyBKs
昨天
1
0
2018个人年终总结

感谢领导的信任和指导,新的一年获得了很多成长和提高,改掉了很多不好的习惯。 在这一年里,我在领导的帮助下,主要完成了以下功能: 1、完成上海银行版本投资营销相关功能的开发。 2、完成车...

万山红遍
昨天
11
0
保密工作与linux系统的发展

保密工作从性质上可以分成商业方面的保密和国家安全方面的保密。由于自己从事的是IT方面的工作,工作中必然会接触涉及到计算机信息方面的相关文件。加上单位已近通过武器装备科研生产单位二级...

linux-tao
昨天
3
0
Spark共享变量

概述 Spark程序的大部分操作都是RDD操作,通过传入函数给RDD操作函数来计算。这些函数在不同的节点上并发执行,但每个内部的变量有不同的作用域,不能相互访问,所以有时会不太方便,Spark提...

仟昭
昨天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部