任何大于页面大小的内容(数组、结构体等)都必须分割成多个页面,因此可能是“虚拟连续的、物理不连续的”。
如果没有更多的知识或限制,任何在其最小对齐方式(例如uint32_t
数组的4字节)和页面大小之间的内容(数组、结构体等)都有可能被分割成多个页面;其中概率取决于其大小和对齐方式。例如,如果页面大小为4096字节,一个数组的最小对齐方式为4字节,大小为4092字节,则有1024中情况中有2种情况会在单个页面上结束(并且有99.8%的机会它将被分割成多个页面)。
任何大小等于其最小对齐方式的内容(变量、小型数组、小型结构体等)都不应该被分割成多个页面(请参见注释3)。
注意1:对于使用从堆中分配的内存的任何内容,可以假定最小对齐方式为堆提供的(实现定义的)最小对齐方式,而不是对象本身的最小对齐方式。例如,对于一个
uint16_t
数组,最小对齐方式将是2字节;但是
malloc()
将返回具有更大对齐方式(可能是16字节)的内存。
注意2:当嵌套时(例如,数组在另一个结构体内部),所有上述内容仅适用于外部结构体。例如,如果您有一个
uint16_t
数组在结构体内,该数组恰好在结构体内的偏移4094处开始;那么很可能数组将被分割成多个页面。
注意3:可以使用指针明确地打破最小对齐方式(例如,使用
malloc()
分配1024字节,然后创建一个指向从分配区域内任意偏移开始的数组的指针)。
注意4:如果某些内容(数组、结构体等)跨越多个页面,那么它们仍有可能是物理上连续的。在最坏情况下,这取决于物理内存的数量(例如,如果计算机有1 GiB可用物理内存和4096字节的页面,则大约有262000分之1的机会两个虚拟连续页面会“意外地物理连续”)。如果操作系统实现了页面/缓存着色(请参见
https://en.wikipedia.org/wiki/Cache_coloring),则可以通过页面/缓存“颜色”的数量来提高“意外地物理连续”的概率(例如,如果计算机有1 GiB可用物理内存和4096字节的页面,并且操作系统使用256个页面/缓存颜色,则大约有1024分之1的机会两个虚拟连续页面会“意外地物理连续”)。
注意5:大多数现代操作系统使用多个页面大小(例如4 KiB页面和2 MiB页面,也可能是1 GiB页面)。这可能使得猜测页面大小变得困难,或者如果假定使用最小的页面大小,则可以提高“意外地物理连续”的概率。
注意6:对于某些CPU(例如最近的AMD / Zen),仅当页表项兼容时(例如,如果4个页表项描述具有相同权限/属性的四个物理连续的4 KiB页面),TLB的行为会像页面更大一样(例如,好像您正在使用16 KiB页面而不是4 KiB页面)。如果操作系统针对这些CPU进行了优化,则结果类似于具有额外页面大小(4 KiB,“16 KiB”,2 MiB甚至1 GiB)。
当在虚拟内存中分配大小为一页的数组时,它必须是一个页面,还是可以分成两个连续的虚拟内存页面(例如第一个页面的底部和第二个页面的顶部)?
当在堆内存中分配大小为一页的数组时,最小对齐是由堆管理器/
malloc()
提供的实现定义的最小对齐(例如,可能是16字节)。然而,大多数现代堆管理器在分配的内存量足够大时切换到使用替代方案(例如
mmap()
或
VirtualAlloc()
或类似方法),因此(取决于实现和他们对“足够大”的定义)它可能被页面对齐。
在使用原始虚拟内存(例如使用或等自己分配数组,而不是使用堆栈和类似的东西)时,页面对齐是有保证的(主要是因为虚拟内存管理器不处理更小的东西)。