内存对齐

(5 mins to read)

内存布局

1
clang++ -Xclang -fdump-record-layouts -c main.cpp
  • 多态对象的起始是虚函数表。
  • 按继承顺序排布各个类,声明顺序排布各个成员,需要结合对齐规则。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
struct BaseA {
BaseA() = default;
virtual ~BaseA() = default;
void FuncA() {}
virtual void testA() {
printf("BaseA testA\n");
}
int a;
int b;
};
struct BaseB {
BaseB() = default;
virtual ~BaseB() = default;
void FuncB(){}
virtual void testB() {
printf("BaseB testB\n");
}
int c;
int d;
};
struct Derive : public BaseA, public BaseB {
virtual void testA() override {
printf("Derive testA\n");
}
virtual void testB() override {
printf("Derive testB\n");
}
};
// -fdump-record-layouts
*** Dumping AST Record Layout
0 | struct Derive
0 | struct BaseA (primary base)
0 | (BaseA vtable pointer)
8 | int a
12 | int b
16 | struct BaseB (base)
16 | (BaseB vtable pointer)
24 | int c
28 | int d
| [sizeof=32, dsize=32, align=8,
| nvsize=32, nvalign=8]
// -fdump-vtable-layouts
Vtable for 'Derive' (11 entries).
0 | offset_to_top (0)
1 | Derive RTTI
-- (BaseA, 0) vtable address --
-- (Derive, 0) vtable address --
2 | Derive::~Derive() [complete]
3 | Derive::~Derive() [deleting]
4 | void Derive::testA()
5 | void Derive::testB()
6 | offset_to_top (-16)
7 | Derive RTTI
-- (BaseB, 16) vtable address --
8 | Derive::~Derive() [complete]
[this adjustment: -16 non-virtual]
9 | Derive::~Derive() [deleting]
[this adjustment: -16 non-virtual]
10 | void Derive::testB()
[this adjustment: -16 non-virtual]

一个小细节:虚析构函数占用了虚函数表中的两个entry

为什么要内存对齐?

  • CPU效率,访存往往会取连续的一段(如64字节)进行cache
  • CPU限制,比如只能以word(如4字节)为单位进行访存。此时非word的倍数就是未对齐访存,有些处理器直接不支持,而支持的处理器也需要更多条指令
  • 并发的false sharing问题

pragma

指示编译器以特定对齐方式对齐结构体成员,其中对齐单位=min(n, 结构体成员的最大大小)

gcc默认n=4。

每个成员的偏移值都是min(对齐单位, 该成员大小)的整数倍,且整个结构体的大小也是对齐单位的整数倍。

1
2
3
4
5
#pragma pack(n)
struct A {

};
alignof(A); // 查看结构体的对齐单位

attribute

1
2
__attribute((packed)); // 设置紧凑排布,即对齐单位为1
__attribute((aligned(x))); // 设置结构体的起始地址以及整体大小均为x的倍数(保证结构体的整体大小是x的倍数可以在使用结构体数组时使得每个结构体都是对齐的)

alignas

作用类似于__attribute((aligned(x)))

1
alignas(x);