数组是连续的吗? (虚拟与物理)
Are Arrays Contiguous? (Virtual vs Physical)
我读到数组在 Virtual 内存中是连续的,但在 Physical 内存中可能不连续,我不明白。
假设我有一个大小为 4KB 的数组(一页 = 一个帧大小),在虚拟内存中该数组是一页。
在虚拟内存中,每一页都转换为一帧,因此我们的数组仍然是连续的...
(在页面 Table 中,我们将页面转换为帧,而不是将每个字节都转换为自己的帧...)
附带问题:(回答此问题时请明确说明这是附带说明):
在一页大小的虚拟内存中分配数组时,它必须是一页还是可以在虚拟内存中分成两个连续的页面(例如第一个页面的下半部分和第二个页面的上半部分)?在这种情况下,最坏的情况是上面的答案是 2,我错了吗?
除非数组的开头恰好与内存页的开头对齐,否则它仍然可以占用两页;它可以从一页的末尾开始,到下一页结束。在堆栈上分配的数组可能不会强制占用单个页面,因为堆栈帧只是在堆栈内存中按顺序分配,并且数组通常在每个堆栈帧中处于相同的偏移量。
堆内存分配器(malloc()
)可以尝试确保小于页面的数组将完全分配在同一页面上,但我'我不确定这是否真的是大多数分配器的实现方式。这样做可能会增加内存碎片。
I read that arrays are contiguous in Virtual Memory but probably not in Physical memory, and I don't get that.
此声明遗漏了一些非常重要的内容。 数组大小
对于小数组,该说法是错误的。对于“large/huge”数组,该语句是正确的。
换句话说:数组被分割成多个不连续的物理页面的概率是数组大小的函数。
对于小数组,概率接近于零,但概率会随着数组大小的增加而增加。当数组大小增加到系统页面大小以上时,概率越来越接近 1。但是需要多个页面的数组在物理内存中可能仍然是连续的。
你的问题:
数组大小等于您的系统页面大小,数组最多可以跨越两个物理页面。
任何大于页面大小的内容(数组、结构...)都必须拆分成多个页面;因此可能是“虚拟连续的,物理上不连续的”。
无进一步了解或限制;任何介于其最小对齐方式(例如 uint32_t
数组的 4 个字节)和页面大小之间的任何内容(数组、结构等)都有可能被拆分到多个页面;其中概率取决于其大小和对齐方式。例如,如果页面大小为 4096 字节,并且数组的最小对齐为 4 字节且大小为 4092 字节,则 1024 中有 2 次机会它最终出现在单个页面上(并且有 99.8% 的机会它会拆分成多个页面)。
大小等于其最小对齐的任何内容(变量、微型数组、微型结构...)都不会(不应该 - 参见注释 3)被拆分到多个页面。
注意 1:对于任何使用从堆分配的内存的东西,最小对齐可以假定为堆提供的(实现定义的)最小对齐,而不是对象本身的最小对齐。例如。对于 uint16_t
的数组,最小对齐为 2 个字节;但是 malloc()
将 return 具有更大对齐的内存(可能是 16 字节)
注意 2:当事物嵌套时(例如,结构内的数组位于另一个结构内),以上所有内容仅适用于外部结构。例如。如果在结构中有一个 uint16_t
的数组,该数组恰好从结构中的偏移量 4094 开始;那么数组被拆分成多个页面的可能性就会大大增加。
注意 3:可以使用指针显式破坏最小对齐(例如,使用 malloc()
分配 1024 字节,然后创建一个指向数组的指针,该数组从分配区域内您想要的任何偏移量开始)。
注4:如果某些东西(数组,结构,...)被拆分到多个页面;那么它有可能在物理上仍然是连续的。在最坏的情况下,这取决于物理内存的数量(例如,如果计算机有 1 GiB 的可用物理内存和 4096 字节的页面,那么在 262000 中大约有 1 次机会 2 个几乎连续的页面将“意外地在物理上连续”)。如果 OS 实现 page/cache 着色(参见 https://en.wikipedia.org/wiki/Cache_coloring ),它会通过 page/cache “颜色”的数量(例如,如果计算机有 1 GiB 的可用物理内存和 4096 字节页面,并且 OS 使用 256 page/cache 颜色,那么在 1024 中大约有 1 次机会 2 个几乎连续的页面将“意外地在物理上连续”)。
注 5:大多数现代操作系统使用多种页面大小(例如 4 KiB 页面和 2 MiB 页面,也可能是 1 GiB 页面)。如果您假设使用了最小的页面大小,这可能会导致难以猜测实际的页面大小,或者提高“物理上意外连续”的可能性。
注意 6:对于某些 CPU(例如最近的 AMD/Zen),当且仅当页面table 条目是兼容的(例如,如果 4 页 table 条目描述了具有相同 permissions/attributes 的四个物理上连续的 4 KiB 页面)。如果 OS 针对这些 CPU 进行了优化,则结果类似于具有额外的页面大小(4 KiB、“16 KiB”、2 MiB,也许还有 1 GiB)。
When allocating array in virtual memory of size one page does it have to be one page or could be split into two contiguous pages in virtual memory (for example bottom half of first one and top half of the second)?
在堆内存中分配一页大小的数组时;最小对齐是堆管理器提供的实现定义的最小对齐/malloc()
(例如,可能是 16 个字节)。然而;当分配的内存量“足够大”时,大多数现代堆管理器转而使用替代方案(例如 mmap()
或 VirtualAlloc()
或类似的);所以(取决于实现和他们对“足够大”的定义)它可能是页面对齐的。
在原始虚拟内存中分配数组时(例如,自己使用 mmap()
或 VirtualAlloc()
或类似的东西,不使用堆,也不使用 malloc()
之类的东西);保证页面对齐(主要是因为虚拟内存管理器不处理任何更小的东西)。
我读到数组在 Virtual 内存中是连续的,但在 Physical 内存中可能不连续,我不明白。
假设我有一个大小为 4KB 的数组(一页 = 一个帧大小),在虚拟内存中该数组是一页。
在虚拟内存中,每一页都转换为一帧,因此我们的数组仍然是连续的...
(在页面 Table 中,我们将页面转换为帧,而不是将每个字节都转换为自己的帧...)
附带问题:(回答此问题时请明确说明这是附带说明):
在一页大小的虚拟内存中分配数组时,它必须是一页还是可以在虚拟内存中分成两个连续的页面(例如第一个页面的下半部分和第二个页面的上半部分)?在这种情况下,最坏的情况是上面的答案是 2,我错了吗?
除非数组的开头恰好与内存页的开头对齐,否则它仍然可以占用两页;它可以从一页的末尾开始,到下一页结束。在堆栈上分配的数组可能不会强制占用单个页面,因为堆栈帧只是在堆栈内存中按顺序分配,并且数组通常在每个堆栈帧中处于相同的偏移量。
堆内存分配器(malloc()
)可以尝试确保小于页面的数组将完全分配在同一页面上,但我'我不确定这是否真的是大多数分配器的实现方式。这样做可能会增加内存碎片。
I read that arrays are contiguous in Virtual Memory but probably not in Physical memory, and I don't get that.
此声明遗漏了一些非常重要的内容。 数组大小
对于小数组,该说法是错误的。对于“large/huge”数组,该语句是正确的。
换句话说:数组被分割成多个不连续的物理页面的概率是数组大小的函数。
对于小数组,概率接近于零,但概率会随着数组大小的增加而增加。当数组大小增加到系统页面大小以上时,概率越来越接近 1。但是需要多个页面的数组在物理内存中可能仍然是连续的。
你的问题:
数组大小等于您的系统页面大小,数组最多可以跨越两个物理页面。
任何大于页面大小的内容(数组、结构...)都必须拆分成多个页面;因此可能是“虚拟连续的,物理上不连续的”。
无进一步了解或限制;任何介于其最小对齐方式(例如 uint32_t
数组的 4 个字节)和页面大小之间的任何内容(数组、结构等)都有可能被拆分到多个页面;其中概率取决于其大小和对齐方式。例如,如果页面大小为 4096 字节,并且数组的最小对齐为 4 字节且大小为 4092 字节,则 1024 中有 2 次机会它最终出现在单个页面上(并且有 99.8% 的机会它会拆分成多个页面)。
大小等于其最小对齐的任何内容(变量、微型数组、微型结构...)都不会(不应该 - 参见注释 3)被拆分到多个页面。
注意 1:对于任何使用从堆分配的内存的东西,最小对齐可以假定为堆提供的(实现定义的)最小对齐,而不是对象本身的最小对齐。例如。对于 uint16_t
的数组,最小对齐为 2 个字节;但是 malloc()
将 return 具有更大对齐的内存(可能是 16 字节)
注意 2:当事物嵌套时(例如,结构内的数组位于另一个结构内),以上所有内容仅适用于外部结构。例如。如果在结构中有一个 uint16_t
的数组,该数组恰好从结构中的偏移量 4094 开始;那么数组被拆分成多个页面的可能性就会大大增加。
注意 3:可以使用指针显式破坏最小对齐(例如,使用 malloc()
分配 1024 字节,然后创建一个指向数组的指针,该数组从分配区域内您想要的任何偏移量开始)。
注4:如果某些东西(数组,结构,...)被拆分到多个页面;那么它有可能在物理上仍然是连续的。在最坏的情况下,这取决于物理内存的数量(例如,如果计算机有 1 GiB 的可用物理内存和 4096 字节的页面,那么在 262000 中大约有 1 次机会 2 个几乎连续的页面将“意外地在物理上连续”)。如果 OS 实现 page/cache 着色(参见 https://en.wikipedia.org/wiki/Cache_coloring ),它会通过 page/cache “颜色”的数量(例如,如果计算机有 1 GiB 的可用物理内存和 4096 字节页面,并且 OS 使用 256 page/cache 颜色,那么在 1024 中大约有 1 次机会 2 个几乎连续的页面将“意外地在物理上连续”)。
注 5:大多数现代操作系统使用多种页面大小(例如 4 KiB 页面和 2 MiB 页面,也可能是 1 GiB 页面)。如果您假设使用了最小的页面大小,这可能会导致难以猜测实际的页面大小,或者提高“物理上意外连续”的可能性。
注意 6:对于某些 CPU(例如最近的 AMD/Zen),当且仅当页面table 条目是兼容的(例如,如果 4 页 table 条目描述了具有相同 permissions/attributes 的四个物理上连续的 4 KiB 页面)。如果 OS 针对这些 CPU 进行了优化,则结果类似于具有额外的页面大小(4 KiB、“16 KiB”、2 MiB,也许还有 1 GiB)。
When allocating array in virtual memory of size one page does it have to be one page or could be split into two contiguous pages in virtual memory (for example bottom half of first one and top half of the second)?
在堆内存中分配一页大小的数组时;最小对齐是堆管理器提供的实现定义的最小对齐/malloc()
(例如,可能是 16 个字节)。然而;当分配的内存量“足够大”时,大多数现代堆管理器转而使用替代方案(例如 mmap()
或 VirtualAlloc()
或类似的);所以(取决于实现和他们对“足够大”的定义)它可能是页面对齐的。
在原始虚拟内存中分配数组时(例如,自己使用 mmap()
或 VirtualAlloc()
或类似的东西,不使用堆,也不使用 malloc()
之类的东西);保证页面对齐(主要是因为虚拟内存管理器不处理任何更小的东西)。