《Go小技巧&易错点100例》第三十一篇

本期分享:

1.Go struct内存对齐

2.使用空结构体(struct{})节省内存

Go struct内存对齐

在计算机系统中,CPU 访问内存时并不是逐字节读取的,而是以特定大小的块(通常为 4/8 字节)为单位进行读取。当数据的内存地址正好是其大小的整数倍时,称为自然对齐。Go 编译器会根据平台特性自动进行内存对齐优化,这种机制虽然可能产生填充字节,但能大幅提升内存访问效率。

内存对齐实战案例
javascript 复制代码
// 未对齐的结构体 (24 bytes)
type BadStruct struct {
    a bool    // 1 byte
    b int64   // 8 bytes
    c bool    // 1 byte
}

// 对齐优化后的结构体 (16 bytes)
type GoodStruct struct {
    b int64   // 8 bytes(偏移量 0)
    a bool    // 1 byte(偏移量 8)
    c bool    // 1 byte(偏移量 9)
    // 自动填充 6 bytes 到 16 bytes(满足 8 字节对齐)
}

func main() {
    fmt.Println(unsafe.Sizeof(BadStruct{}))  // 输出 24
    fmt.Println(unsafe.Sizeof(GoodStruct{})) // 输出 16
}

优化原理

BadStructac 导致 b 需要填充 7 字节才能对齐

GoodStruct 通过字段排序减少填充,内存占用降低 33%

对齐规则总结

1)结构体整体大小需是最大字段对齐值的整数倍

2)每个字段的偏移量必须能整除其类型大小

3)嵌套结构体继承父结构体的对齐规则

使用空结构体(struct{})节省内存

struct{} 是 Go 语言中唯一零内存的类型:

javascript 复制代码
fmt.Println(unsafe.Sizeof(struct{}{})) // 输出 0
六大应用场景

1)场景1:实现高效集合(Set)

javascript 复制代码
type Set map[string]struct{}

func (s Set) Add(key string) {
    s[key] = struct{}{}
}

func (s Set) Contains(key string) bool {
    _, ok := s[key]
    return ok
}

// 使用示例
s := make(Set)
s.Add("apple")
fmt.Println(s.Contains("apple")) // true

2)场景2:通道信号传递

javascript 复制代码
func worker(stopCh <-chan struct{}) {
    for {
        select {
        case <-stopCh:
            return
        default:
            // 执行任务
        }
    }
}

// 发送关闭信号
closeCh := make(chanstruct{})
go worker(closeCh)
close(closeCh) // 广播关闭

3)场景3:方法接收器(无状态)

javascript 复制代码
type Logger struct{}

func (Logger) Info(msg string) {
    fmt.Printf("[INFO] %s\n", msg)
}

// 使用零内存接收器
var log Logger
log.Info("service started")

4)场景4:占位通道

javascript 复制代码
// 限制并发数为 10
sem := make(chan struct{}, 10)
for i := 0; i < 1000; i++ {
    sem <- struct{}{}
    go func() {
        defer func() { <-sem }()
        // 业务逻辑
    }()
}

5)场景5:接口实现标记

javascript 复制代码
type Marker interface {
    isMarker()
}

type MyMarker struct{}

func (MyMarker) isMarker() {}

// 类型断言检查
func CheckMarker(v interface{}) bool {
    _, ok := v.(Marker)
    return ok
}

6)场景6:JSON 空对象

javascript 复制代码
type Response struct {
    Data  interface{} `json:"data"`
    Error struct{}    `json:"error,omitempty"`
}

// 序列化时自动忽略空 error 字段
resp := Response{Data: "success"}
jsonData, _ := json.Marshal(resp) // {"data":"success"}
注意事项

1)空结构体作为结构体字段时会产生对齐填充

javascript 复制代码
type Wrapper struct {
   _ struct{} // 0 字节
   n int64    // 8 字节(偏移量 0)
}
fmt.Println(unsafe.Sizeof(Wrapper{})) // 8 bytes

2)不同地址的空结构体实例本质相同

javascript 复制代码
a := struct{}{}
b := struct{}{}
fmt.Println(&a == &b) // 输出 true(编译器优化)

本篇结束~

相关推荐
Victor3561 小时前
Redis(42)Redis集群如何处理键的迁移?
后端
wheeldown3 小时前
【Linux】为什么死循环卡不死 Linux?3 个核心逻辑看懂进程优先级与 CPU 调度密码
linux·运维·服务器·开发语言·c++·unix·进程
程序员爱钓鱼3 小时前
Go语言实战案例- Redis实现简单排行榜
后端·google·go
xxy.c3 小时前
嵌入式解谜日志-网络编程(udp,tcp,(while循环原理))
linux·运维·c语言·开发语言·数据结构
Want5953 小时前
C/C++哆啦A梦
c语言·开发语言·c++
angushine3 小时前
Spring Boot 工程启动时自动执行任务方法
java·spring boot·后端
Nexmoe4 小时前
我踩过最深的 React 数据沉钻坑,以及我现在偷懒写法
开发语言·javascript·ecmascript
野犬寒鸦5 小时前
力扣hot100:缺失的第一个正数(哈希思想)(41)
java·数据结构·后端·算法·leetcode·哈希算法
计算机毕业设计木哥5 小时前
计算机Python毕业设计推荐:基于Django的酒店评论文本情感分析系统【源码+文档+调试】
开发语言·hadoop·spring boot·python·spark·django·课程设计