您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > Go语言

GO 内存对齐

时间:2021-02-25 11:25:20  来源:  作者:

前言

之前遇到过这样一个情况(发现问题的结构体并不长这样, 不过为了引出问题, 改了一下):

type Test struct {	
  b bool
  i3 int32
  i8 int8
  i64 int64
  by byte
}

func main() {
  t := Test{}
  fmt.Printf("%d", unsafe.Sizeof(t))
}

创建一个结构体, 查看一下其内存占用. 看结果前先简单算一下:

  • bool: 1B
  • int32: 4B
  • int8: 1B
  • int64: 8B
  • byte: 1B

这么算下来的话, Test结构体占用应该是: 1+4+1+8+1=15B. 15个字节对吧. 来, 打印看一下:

GO 内存对齐

 

32个字节???这不坑我么.内存占用直接多出一倍.

探索

通过查找资料, 发现了这样一个名词: 内存对齐. 什么是内存对齐呢?

简单说, 就是CPU在读取数据的时候, 并不是一个字节一个字节读取的, 而是一块一块读取的. 那么这个快是多大呢? 根据CPU位数不同而不同.

而GO编译器在编译的时候, 为了保证内存对齐, 对每一个数据类型都给出了对齐保证, 将未对齐的内存留空. 如果一个类型的对齐保证是4B, 那么其数据存放的起始地址偏移量必是4B 的整数倍. 而编译器给出的这个对齐保证是多少呢? 不同版本不同平台的编译器不尽相同, 可以通过函数unsafe.Alignof 来获取.

通过分析之前的数据结果, 就能大致理解了. 先来看一下几个类型对齐保证的值:

fmt.Printf("bool: %dn", unsafe.Alignof(bool(false)))	
fmt.Printf("int32: %dn", unsafe.Alignof(int32(0)))	
fmt.Printf("int8: %dn", unsafe.Alignof(int8(0)))	
fmt.Printf("int64: %dn", unsafe.Alignof(int64(0)))
fmt.Printf("byte: %dn", unsafe.Alignof(byte(0)))

结果如下:

GO 内存对齐

 

来尝试一个一个放到内存中(下图中每个空白代表一个字节):

1.放入bool: 其对齐保证为1, 第一个变量, 直接放入即可.

GO 内存对齐

 

2.放入int32. 其对齐保证为4, 既偏移量为4的整数倍. 而现有地址中, 首个4的整数倍为第四个字节(中间三字节留空).

GO 内存对齐

 

按照这个思路, 依次将后面的变量放入, 结果占用的内存为(其中字母依次为变量占用, X为对齐留空):

AXXX BBBB CXXX XXXX DDDD DDDD E

但是这才25个字节啊. 和实际的32字节还差点呢. 别急, 再看一下结构体的对齐保证, 发现是8B. 上面不是8B 的整数倍, 往后补零. 结果:

AXXX BBBB CXXX XXXX DDDD DDDD EXXX XXXX

如此一来, 就正好32位了. 结构体的对齐保证, 为其成员变量对齐保证的最大值.

why

那么编译器为什么要做内存对齐这种事情呢? 举个例子, 如果不做内存对齐, 那么下面这个结构体的内存分布为:

type Test struct {	
  b   bool
  i3  int32
}

ABBB B

还记得之前说, CPU读取内存是一块一块读取的么? 而这个块, 假设是4B.

这样的话, 当你需要读取i3变量的时候, 需要进行两次内存访问. 而对齐之后, 只需要进行一次内存访问即可. 是典型的空间换时间的做法.

修改

既然知道了问题出在哪里, 那么是不是如果换一下字段的存放顺序, 就可以压缩内存空间了呢? 思路很简单, 将对齐保证小的放到前面, 试一下:

type Test struct {	
  b   bool
  by  byte
  i8  int8
  i3  int32
  i64 int64
}
func main() {
  t := Test{}	
  fmt.Printf("%d", unsafe.Sizeof(t))
}
GO 内存对齐

 

通过之前的对齐分析. 结果确为18B. 也就是因为字段顺序的问题, 编译器为了保证内存对齐, 向其中填充了很多空白, 造成了内存的浪费.

仅仅是修改了一下字段的顺序, 就可以将结构体的内存占用直接降低一倍. 见识了...

检测工具

那么, 有没有什么办法能够帮我们检测是否存在内存对齐的优化呢? 毕竟平常写的时候, 谁会关心这玩意呢. 别说, 还真有. golangci-lint

官网: https://golangci-lint.run/

安装: brew install golangci-lint

检测所有文件命令: golangci-lint run ./..

检测一下最开始的结构体文件(添加参数指定检测内存对齐):

golangci-lint run --disable-all -E maligned main.go

看到结果:

GO 内存对齐

 

会看到提示, 该结构体当前占有32B, 可优化至16B. 完美.

当然, 此工具的功能不仅如此, 它能够提供很多建议, 有待发掘.


其实, 项目中估计也很少有关注内存对齐的时候吧. 不过毕竟积少成多, 内存这玩意, 能省则省嘛.



Tags:内存对齐   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
前言之前遇到过这样一个情况(发现问题的结构体并不长这样, 不过为了引出问题, 改了一下):type Test struct { b bool i3 int32 i8 int8 i64 int64 by byte}func main(...【详细内容】
2021-02-25  Tags: 内存对齐  点击:(221)  评论:(0)  加入收藏
▌简易百科推荐
zip 是一种常见的归档格式,本文讲解 Go 如何操作 zip。首先看看 zip 文件是如何工作的。以一个小文件为例:(类 Unix 系统下)$ cat hello.textHello!执行 zip 命令进行归档:$ zip...【详细内容】
2021-12-17  Go语言中文网    Tags:Go语言   点击:(12)  评论:(0)  加入收藏
大家好,我是 polarisxu。前段时间,Russ Cox 明确了泛型相关的事情,原计划在标准库中加入泛型相关的包,改放到 golang.org/x/exp 下。目前,Go 泛型的主要设计者 ianlancetaylor 完...【详细内容】
2021-11-30  Go语言中文网    Tags:slices 包   点击:(24)  评论:(0)  加入收藏
前言最近因为项目需要写了一段时间的 Go ,相对于 Java 来说语法简单同时又有着一些 Python 之类的语法糖,让人大呼”真香“。 但现阶段相对来说还是 Python 写的多一些,偶尔还...【详细内容】
2021-11-25  crossoverJie    Tags:Go   点击:(29)  评论:(0)  加入收藏
go-micro是基于 Go 语言用于开发的微服务的 RPC 框架,主要功能如下:服务发现,负载均衡 ,消息编码,请求/响应,Async Messaging,可插拔接口,最后这个功能牛p安装步骤安装proto...【详细内容】
2021-09-06    石老师小跟班  Tags:go-micro   点击:(196)  评论:(0)  加入收藏
GoLand 2021.2 EAP 5 现已发布。用户可以从工具箱应用程序中获得 EAP 构建,也可以从官方网站手动下载。并且从此 EAP 开始,只有拥有有效的 JetBrains 帐户才能加入该计划。手...【详细内容】
2021-06-29  IT实战联盟  今日头条  Tags:GoLand   点击:(185)  评论:(0)  加入收藏
作者:HDT3213今天给大家带来的开源项目是 Godis:一个用 Go 语言实现的 Redis 服务器。支持: 5 种数据结构(string、list、hash、set、sortedset) 自动过期(TTL) 发布订阅、地理位...【详细内容】
2021-06-18  HelloGitHub  今日头条  Tags:Go   点击:(125)  评论:(0)  加入收藏
统一规范篇合理规划目录本篇主要描述了公司内部同事都必须遵守的一些开发规矩,如统一开发空间,既使用统一的开发工具来保证代码最后的格式的统一,开发中对文件和代码长度的控制...【详细内容】
2021-05-18  1024课堂    Tags:Go语言   点击:(232)  评论:(0)  加入收藏
闭包概述 闭包不是Go语言独有的概念,在很多编程语言中都有闭包 闭包就是解决局部变量不能被外部访问的一种解决方案 是把函数当作返回值的一种应用 代码演示总体思想:在函数...【详细内容】
2021-05-14  HelloGo  今日头条  Tags:Go语言   点击:(223)  评论:(0)  加入收藏
一时想不开,想了解一下Go语言,于是安装了并体验了一下。下载1. 进入golang.google.cn 点击Download Go 2.选择对应的操作系统,点击后开始下载。 安装1. windows下执行傻瓜式安...【详细内容】
2021-05-12  程序员fearlazy  fearlazy  Tags:Go语言   点击:(236)  评论:(0)  加入收藏
1.简介channel是Go语言的一大特性,基于channel有很多值得探讨的问题,如 channel为什么是并发安全的? 同步通道和异步通道有啥区别? 通道为何会阻塞协程? 使用通道导致阻塞的协程...【详细内容】
2021-05-10  程序员麻辣烫  今日头条  Tags:Go通道   点击:(272)  评论:(0)  加入收藏
相关文章
    无相关信息
最新更新
栏目热门
栏目头条