内存物理构造
内存的外形一般如下
一个内存是由若干个内存颗粒构成的,每一个内存颗粒叫做一个chip,是由8个bank组成的。
构造如图:
而每一个bank是一个二维平面上的矩阵,矩阵的每一个元素都是保存一个字节,也就是8个bit。
内存编址方式
那么对于我们在应用程序中内存中地址连续的8个字节,例如0x0000-0x0007,直观感觉,应该是在第一个bank上吗? 其实不是的,程序员视角看起来连续的地址0x0000-0x0007,实际上位于8个bank中的,每一个bank只保存了一个字节。在物理上,他们并不连续。下图很好地阐述了实际情况
原因是电路工作效率。内存中的8个bank是可以并行工作的。 如果你想读取址0x0000-0x0007,每个bank工作一次,拼起来就是你要的数据,IO效率会比较高。但要存在一个bank里,那这个bank只能自己干活。只能串行进行读取,需要读8次,这样速度会慢很多。
结论
所以,内存对齐最最底层的原因是内存的IO是以8个字节64bit为单位进行的。 对于64位数据宽度的内存,假如cpu也是64位的cpu(现在的计算机基本都是这样的),每次内存IO获取数据都是从同行同列的8个chip中各自读取一个字节拼起来的。从内存的0地址开始,0-7字节的数据可以一次IO读取出来,8-15字节的数据也可以一次读取出来。
换个例子,假如你指定要获取的是0x0001-0x0008,也是8字节,但是不是0开头的,内存需要怎么工作呢?没有好办法,内存只好先工作一次把0x0000-0x0007取出来,然后再把0x0008-0x0015取出来,把两次的结果都返回给你。 CPU和内存IO的硬件限制导致没办法一次跨在两个数据宽度中间进行IO。这样你的应用程序就会变慢,算是计算机因为你不懂内存对齐而给你的一点点惩罚。
扩展1:事实上,编译和链接器会自动替开发者对齐内存的,尽量帮你保证一个变量不跨列寻址。但是他不能做到十分完美。
扩展2:其实在内存硬件层上,还有操作系统层。操作系统还管理了CPU的一级、二级、三级缓存。高速缓存里的Cache Line也是64字节,它是内存IO的整数倍,不会让内存IO浪费。
整理自“开发内功修炼”@知乎