内存对齐,即字节对齐,指代码编译后在内存的布局与使用方式。现代计算机多为32位或64位地址对齐,若变量内存未对齐,可能触发总线错误。
CPU访问内存是以字长为单位,而非逐个字节。例如32位CPU,字长为4字节,其访问内存单位也是4字节。这种设计旨在减少CPU访问内存次数,提升访问吞吐量。若不进行内存对齐,会增加CPU访问内存次数,降低性能。同时,内存对齐对实现变量的原子性操作也有益处,若变量大小不超过字长,内存对齐后,对该变量的访问就是原子的,这在并发场景下至关重要。
内存对齐虽能提升性能,但需付出代价。因变量间增加填充,未存储真实有效数据,故占用内存会更大,属于“空间换时间”策略。
类型 | 大小 |
---|---|
bool |
1个字节 |
intN, uintN, floatN, complexN |
N/8个字节(例如float64是8个字节) |
int, uint, uintptr |
1个字 |
*T |
1个字 |
string |
2个字(数据、长度) |
[]T |
3个字(数据、长度、容量) |
map |
1个字 |
func |
1个字 |
chan |
1个字 |
interface |
2个字(类型、值) |
字长为4字节时,1个字是4字节;字长为8字节时,1个字是8字节。
package performance
import (
"testing"
)
// 占用32个字节
type person struct {
hasMoney bool // 1个字节
name string // 16个字节
age int16 // 2个字节
}
func Benchmark_Alignment(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = make([]person, b.N)
}
}
运行测试命令:
$ go test -run='^$' -bench=. -count=1 -benchtime=10000x -benchmem > slow.txt
package performance
import (
"testing"
)
// 占用24个字节
type person struct {
name string // 16个字节
age int16 // 2个字节
hasMoney bool // 1个字节
}
func Benchmark_Alignment(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = make([]person, b.N)
}
}
运行测试命令:
$ go test -run='^$' -bench=. -count=1 -benchtime=10000x -benchmem > fast.txt
$ benchstat -alpha=100 slow.txt fast.txt
输出结果如下:
name old time/op new time/op delta
_Alignment-8 18.1µs ± 0% 15.2µs ± 0% -15.80% (p=1.000 n=1+1)
name old alloc/op new alloc/op delta
_Alignment-8 328kB ± 0% 246kB ± 0% -25.00% (p=1.000 n=1+1)
name old allocs/op new allocs/op delta
_Alignment-8 1.00 ± 0% 1.00 ± 0% ~ (all equal)
采用内存对齐方案后,运行时间提升了15%,内存分配优化了25%。
空结构体struct{}
大小为0。当结构体中字段类型为struct{}
时,一般无需内存对齐,但若最后一个字段类型为struct{}
,则需内存对齐。若内存未对齐且有指针指向结构体最后一个字段,指针对应地址将达结构体之外,虽Go保证无法对该指针进行操作,但若该指针一直存活不释放内存,会产生内存泄露问题。良好实践是:不要将struct{}
类型的字段放在结构体最后,以避免内存对齐带来的占用损耗。
package main
import (
"fmt"
"unsafe"
)
type t1 struct {
x int32
y struct{}
}
type t2 struct {
y struct{}
x int32
}
func main() {
fmt.Printf("size = %d\n", unsafe.Sizeof(t1{}))
fmt.Printf("size = %d\n", unsafe.Sizeof(t2{}))
}
运行结果:
size = 8
size = 4
将struct{}
类型的字段从最后一个换到第一个,避免了内存对齐,节省了一半内存使用量。
如果您喜欢我的文章,请点击下面按钮随意打赏,您的支持是我最大的动力。
最新评论