首页 > 系统相关 >go语言内存对齐

go语言内存对齐

时间:2023-04-10 21:55:12浏览次数:40  
标签:读取 32 64 内存 go 对齐 字节

go语言内存对齐

什么是内存对齐

内存对齐,是代码编译后在内存的布局和使用方式。当一个内存地址a是n字节的倍数(其中n是2的幂)时,内存地址a被称为n字节对齐。编译器会将数据按照特定的规则,把数据安排到合适的存储地址上,并占用合适的地址长度

比如:存储int32位和int64位的数据时

img

没有进行内存对齐,int64会紧跟着int32位进行内存分配

img

进行内存对齐,将会进行字节填充,使得int64能够从内存地址为8的倍数开始存储

为什么要进行内存对齐

  1. 跨平台问题:如果数据不对齐,那么在64位字长机器存储的数据可能在32位字长的机器可能就无法正常的读取。

  2. 性能问题:如果不对齐,那么每个数据要通过多少次总线传输是未知的,如果每次都要处理这些复杂的情况,那么数据的读/写性能将会收到很大的影响。之所以有些CPU支持访问任意地址,是因为处理器在后面多做了很多额外处理。

一看感觉字节对齐后浪费了内存, 但是当我们去读取内存中的数据给CPU时,64位的机器(一次可以原子读取8字节)在内存对齐和不对齐的情况下A变量都只需要原子读取一次就行, 但是对齐后B变量的读取只需一次, 而不对齐的情况下,B需要读取2次,且需要额外的处理牺牲性能来保证2次读取的原子性。所以本质上,内存填充是一种以空间换时间, 通过额外的内存填充来提高内存读取的效率的手段。

内存对齐规则

基本数据类型

数据类型 类型大小(32位/64位) 最大对其边界(32位) 最大对齐边界(64位)
int8/uint8/byte 1字节 1 1
int16/uint16 2字节 2 2
int32/uint32/rune/float32/complex32 4字节 4 4
int64/uint64/float64/complex64 8字节 4 8
string 8字节/16字节 4 8
slice 12字节/24字节 4 8

go语言的基本类型的内存对齐是按照基本类型的大小和机器字长中最小值进行对齐,可以看出,如果32位在读取int64等8字节长度的数据时,是无法通过内存对齐保证读取的原子性的。

结构体数据类型

go语言的结构体的对齐是先对结构体的每个字段进行对齐,然后对总体的大小按照最大对齐边界的整数倍进行对齐。空结构体理论上不占用字节,有一个特殊的情况就是,如果空结构体嵌套到一个结构体尾部,那么这个结构体也是要额外对齐的,因为如果有指针指向该字段, 返回的地址将在结构体之外,如果此指针一直存活不释放对应的内存,就会有内存泄露的问题。

原子操作问题

对于前面讲到的32位机器读取int64数据的原子操作问题,在atomic包中有下面一段描述

On 386, the 64-bit functions use instructions unavailable before the Pentium MMX.

On non-Linux ARM, the 64-bit functions use instructions unavailable before the ARMv6k core.

On ARM, 386, and 32-bit MIPS, it is the caller's responsibility to arrange for 64-bit alignment of 64-bit words accessed atomically. The first word in a variable or in an allocated struct, array, or slice can be relied upon to be 64-bit aligned.

在ARM,386,和32位MIPS,调用者有责任安排原子访问的64位字按照8字节对齐,否则程序会panic

开辟的结构体、数组和切片值中的第一个(64位)字可以被认为是8字节对齐的。被开辟可以解读为一个声明的变量、内置函数make或new返回的引用的值, 如果一个切片是从一个开辟的数组派生出来的并且此切片和此数组共享第一个元素,则我们也可以将此切片看作是一个开辟的值。

总结

  1. 内存对齐可以使cpu更高效的访问内存中的数据
  2. 可以合理的安排结构体内的字段,来节省内存
  3. 空结构体不要放在结构体的最后,防止内存浪费
  4. 32位系统上对于64位的原子访问,需要保证8字节对齐

标签:读取,32,64,内存,go,对齐,字节
From: https://www.cnblogs.com/zpf253/p/17303785.html

相关文章

  • CPP内存管理
    从内核到C++应用整个系统的不同层级有着不同的内存管理器。linux内核:伙伴系统(以页为单位进行管理)、slab分配器(定制化的内存管理器)。malloc\free库函数:使用系统调用mmap、sbrk,以及bins管理多个空闲链表(内存池)。有合并空闲块的操作。有两种方式管理内存:如果分配内存大于mmap......
  • InnoDB引擎之内存与磁盘结构
     一、逻辑存储结构      1、表空间(Tablespace)表空间(Tablespace)是一个逻辑容器,在一个表空间中可以有一个或多个段,一个段只能属于一个表空间。数据库由一个或多个表空间组成,表空间从管理上可以划分为系统表空间、用户表空间、撤销表空间、临时表空间等。 ......
  • Django笔记二十一之使用原生SQL查询数据库
    本文首发于公众号:Hunter后端原文链接:Django笔记二十一之使用原生SQL查询数据库Django提供了两种方式来执行原生SQL代码。一种是使用raw()函数,一种是使用connection.cursor()。但是官方还是推荐在使用原生SQL之前,尽量的先去探索一下QuerySet提供的各种API。目前......
  • 【享元设计模式详解】C/Java/JS/Go/Python/TS不同语言实现
    简介享元模式(FlyweightPattern),是一种结构型设计模式。主要用于减少创建对象的数量,以减少内存占用和提高性能。它摒弃了在每个对象中保存所有数据的方式,通过共享多个对象所共有的相同状态,让你能在有限的内存容量中载入更多对象。当程序需要生成数量巨大的相似对象时,可能对内存有......
  • Excelize 发布 2.7.1 版本,Go 语言 Excel 文档基础库
    Excelize是Go语言编写的用于操作OfficeExcel文档基础库,基于ECMA-376,ISO/IEC29500国际标准。可以使用它来读取、写入由MicrosoftExcel™2007及以上版本创建的电子表格文档。支持XLAM/XLSM/XLSX/XLTM/XLTX等多种文档格式,高度兼容带有样式、图片(表)、透视表......
  • Go笔记(一):基本数据类型
    前言Go官方编程指南:https://golang.google.cn/doc/tutorial/       选择中文阅读go的官方文档。      查看官方提供的go的包的函数使用,即API(applicationprograminterface)应用程序接口,就是go的各个包的各个函数:https://golang.google.......
  • 用 Go 剑指 Offer 12. 矩阵中的路径
    给定一个 mxn二维字符网格 board和一个字符串单词 word。如果 word存在于网格中,返回true;否则,返回false。单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。 例如,......
  • GORM自定义类型
    GORM自定义类型官网地址官方也有一个用来收集自定义的类型的仓库:https://github.com/go-gorm/datatypes场景时间类型初始的时间类型只有time.time类型,而我们习惯输入和展示的结构是形如2023-04-0812:12:12这种。这种格式需要被程序转化为time.time类型被orm使用,从数据库......
  • JVM 堆内存大小查看
    JVM堆内存大小查看在默认不配置JVM堆内存大小的情况下,JVM根据默认值来配置当前内存大小,可通过如下命令进行查看:java-XX:+PrintFlagsFinal-version|grepHeapSize  上图表示启动的JVM默认最大堆内存约为2.9G,初始化大小为195MB。 ......
  • 不想做架构师的Gopher不是好程序员
    最近我们在组队学习《手把手带你写一个web框架》,强制PUSH,坚持每天学习打卡,不完成惩罚发红包的那种。你别说,效果还真挺好。昨天学到了架构部分,很受启发,光学不写假把式。(还是得坚持输出哇)我站在大佬的肩膀上输出一篇总结文章出来,希望对大家有帮助:概述所谓架构,与一线开发最大......