数组内存分配-分页

本文关键字:-分页 分配 内存 数组 | 更新日期: 2023-09-27 18:12:20

不确定Java、c#和c++的答案是否相同,所以我把它们都分类了。所有语言的答案最好。

所有的天我一直在想,如果我分配数组所有的单元格将在一个连续的空间。因此,如果系统中没有足够的内存,就会抛出内存溢出异常。

我说的对吗?或者是否有可能,分配的数组将被分页?

数组内存分配-分页

c++数组是连续的,这意味着内存有连续的地址,也就是说它在虚拟地址空间中是连续的。它不需要在物理地址空间中是连续的,因为现代处理器(或它们的内存子系统)有一个将虚拟页与物理页关联起来的大映射。在用户模式下运行的进程永远不会看到其数组的物理地址。

我认为在实践中大多数或所有的Java实现是相同的。但是,程序员永远不会看到数组元素的实际地址,而只是数组的引用和对其进行索引的方法。因此,从理论上讲,Java实现可以拆分数组并在[]操作符中隐藏这一事实,尽管JNI代码仍然可以以c++风格查看数组,此时需要一个连续的块。这是假设JVM规范中没有关于数组布局的任何内容,而jarnbjo告诉我没有。

我不了解c#,但我希望情况与Java非常相似-您可以想象实现可能使用[]操作符来隐藏数组在虚拟地址空间中不连续的事实。一旦有人得到了一个指针,这种伪装就会失败。[编辑:多项式说数组在c#中可以不连续,直到有人把它们钉住,这是有意义的,因为你知道你必须在将它们传递到使用地址的低级代码之前把对象钉住。]

注意,如果你分配一个大型对象类型的数组,那么在c++中,数组实际上是许多大型结构体端到端的排列,因此所需的连续分配大小取决于对象的大小。在Java中,对象数组"实际上"是一个引用数组。这是一个比c++数组更小的连续块。对于原生类型,它们是相同的。

在c#中你不能保证内存块是连续的。CLR试图在一个连续的块中分配内存,但它也可能在几个块中分配内存。关于CLR应该如何管理c#内存,几乎没有定义行为,因为它被设计为被托管结构抽象掉。

在c#中唯一重要的是,如果你通过p/Invoke将数组作为指针传递给一些非托管代码,在这种情况下,你应该使用GC.Pin来锁定对象在内存中的位置。也许其他人能够解释CLR和GC在这种情况下如何处理对连续内存的需求。

我说的对吗?

在Java和c#中为True,但c++只在达到进程或系统限制时才会得到错误。不同之处在于,在Java和c#中,应用程序对自己施加了限制。在c++中,这个限制是由操作系统施加的。

或者是否有可能,分配的数组将被分页?

这也是可能的。然而,在Java中,对堆进行分页对性能非常不利。当GC运行时,所有被检查的对象都必须在内存中。在c++中,它不是很好,但影响较小。

如果你想要在Java中分页的大型结构,你可以使用ByteBuffer.allocateDirect()或内存映射文件。这是通过使用堆外的内存来实现的(基本上就是c++使用的)

在C(++)程序中,通常(也就是说,除非我们讨论的是解释代码而不是直接编译和执行代码)数组在虚拟地址空间中是连续的(当然,如果所讨论的平台上有这样的东西)。

在这里,如果一个大数组不能连续分配,即使有足够的空闲内存,您将得到std::bad_alloc异常(在c++中)或NULL(来自C/c++中类似malloc()的函数或c++中的非抛出操作符new)。

虚拟内存(和对磁盘的分页)通常不能解决虚拟地址空间碎片问题,或者至少不能直接解决,它的目的是不同的。它通常用来让程序认为有足够的内存,而实际上没有。由于操作系统必须在内存压力下在RAM和磁盘之间交换数据,因此RAM可以通过空闲磁盘空间有效地扩展,但代价是性能降低。

您的数组(部分或全部)可以由操作系统卸载到磁盘。但是这对你的程序来说是透明的,因为当它需要从数组中访问一些东西时,操作系统会将它加载回来(同样,部分或全部,根据操作系统认为必要)。

在没有虚拟内存的系统上,没有虚拟地址到物理地址的转换,你的程序将直接使用物理内存,因此,它将不得不处理物理内存碎片,并与其他程序竞争空闲内存和地址空间。使分配失败更容易发生(具有虚拟内存的系统通常在单独的虚拟地址空间中运行程序,并且应用程序A的虚拟地址空间中的碎片不会影响应用程序B的虚拟地址空间)。

当然可以用Java和c#。我们可以通过在内存页大小为4096字节的Windows机器上运行byte[] array = new byte[4097];来展示这一点。因此,它必须在不止一页中。

当然分页会影响性能,但这可能是使用。net或Java等框架的GC具有优势的情况之一,因为GC是由了解分页发生的人编写的。结构体仍然有一些优势,使它更有可能在同一页面上拥有相关元素(与指针跟踪集合相比,更倾向于数组支持的集合)。这在CPU缓存方面也有优势。(大数组仍然是导致堆碎片的最好方法之一,这是GC必须努力解决的问题,但是由于GC非常擅长这样做,因此它仍然将胜过许多其他处理相同问题的方法)。

在c++中几乎可以肯定,因为我们通常在操作系统的内存管理级别上编写代码——数组位于连续的虚拟空间(无论是在堆上还是堆栈上),而不是连续的物理空间。在C或c++中,可以编写低于此级别的代码,但这通常只由实际编写内存管理代码的人自己完成。