不确定Java,C#和C ++的答案是否相同,所以我对所有答案进行了分类。所有语言的答案都会很好。
我一直在想,如果我分配数组,那么所有单元格都将位于一个连续的空间中。因此,如果系统中没有足够的内存,则会出现内存不足异常。
没问题,我说的是什么?或者是否有可能,分配的数组将被分页?
答案 0 :(得分:5)
C ++数组是连续的,这意味着内存具有连续的地址,即它在虚拟地址空间中是连续的。它在物理地址空间中不需要是连续的,因为现代处理器(或它们的存储器子系统)具有将虚拟页面与物理页面相关联的大映射。在用户模式下运行的进程永远不会看到其数组的物理地址。
我认为在实践中大多数或所有Java实现都是相同的。但程序员永远不会看到数组元素的实际地址,只是对数组的引用和索引它的方法。因此从理论上讲,Java实现可能会破坏数组并在[]
运算符中隐藏该事实,尽管JNI代码仍然可以以C ++样式查看数组,此时需要连续的块。这假设JVM规范中没有关于数组布局的内容,jarnbjo告诉我没有。
我不知道C#,但我希望情况与Java非常相似 - 您可以想象一个实现可能会使用[]
运算符来隐藏数组在虚拟地址中不连续的事实空间。一旦有人获得指针,假装就会失败。 [编辑:Polynomial说C#中的数组可能是不连续的,直到有人将它们固定为止,这是有道理的,因为你知道你必须在将对象传递给使用地址的低级代码之前将其固定。]
请注意,如果您分配一些大型对象类型的数组,那么在C ++中,数组实际上是端到端放置的许多大型结构,因此连续分配所需的大小取决于对象的大小。在Java中,对象数组“实际上”是一个引用数组。所以这是一个比C ++数组更小的连续块。对于本地类型,它们是相同的。
答案 1 :(得分:2)
在C#中,您不能保证内存块是连续的。 CLR尝试在一个连续的块中分配内存,但它可以在几个块中分配它。关于CLR如何管理C#内存几乎没有明确的行为,因为它被设计为被托管结构抽象出来。
在C#中唯一真正重要的是你是通过P / Invoke将数组作为指针传递给某些非托管代码,在这种情况下你应该使用GC.Pin
来锁定对象在内存中的位置。在这种情况下,也许其他人将能够解释CLR和GC如何处理连续内存的需求。
答案 2 :(得分:1)
没问题,我说的是什么?
是的,在Java和C#中,但只有在达到流程或系统限制时,C ++才会出错。不同的是,在Java和C#中,应用程序对自身施加了限制。在C ++中,操作系统强加了限制。
或者有可能,分配的数组会被分页吗?
这也是可能的。但是在Java中,对页面进行分页会对性能造成很大影响。 GC运行时,所有检查的对象必须在内存中。在C ++中它不是很好,但影响较小。
如果您想要可以在Java中分页的大型结构,可以使用ByteBuffer.allocateDirect()或内存映射文件。这可以通过在堆上使用内存来实现(基本上是C ++使用的)
答案 3 :(得分:1)
在java的情况下,Array实现为Object ....
和对象只在堆中获得m / m ...
所以我不完全确定但是......只在RAM中制作....
你可以检查.. IBM M/m
答案 4 :(得分:1)
通常在C(++)程序中(也就是说,除非我们讨论解释代码而不是编译代码并直接执行它),否则数组在虚拟地址空间中是连续的(当然,如果有这样的话)有问题的平台上的东西)。
在那里,如果一个大数组不能连续分配,即使有足够的可用内存,你也会得到std :: bad_alloc异常(在C ++中)或NULL(来自malloc() - 就像C /中的函数一样C ++中的C ++或非抛出运算符new。
虚拟内存(以及对磁盘的分页)通常不能解决虚拟地址空间碎片问题,或者至少不能直接解决,其目的不同。它通常用于让程序认为有足够的内存,而实际上却没有。 RAM可以通过可用磁盘空间进行有效扩展,但性能较低,因为操作系统必须在内存压力下在RAM和磁盘之间交换数据。
操作系统可以将您的阵列(部分或全部)卸载到磁盘。但这对您的程序是透明的,因为无论何时需要从数组中访问某些内容,操作系统都会将其加载回来(再次,部分或整体,如操作系统认为必要)。
在没有虚拟内存的系统上,没有虚拟到物理地址转换,你的程序将直接与物理内存一起工作,因此,它必须处理物理内存碎片,并且还要与其他程序竞争可用内存和地址空间,使得分配失败更容易发生(具有虚拟内存的系统通常在单独的虚拟地址空间中运行程序,而应用程序A的虚拟地址空间中的碎片不会影响应用程序B的碎片)。
答案 5 :(得分:1)
使用Java和C#肯定。我们可以通过在内存页面大小为4096bytes的Windows机器上运行byte[] array = new byte[4097];
来显示此信息。因此必须在一页以上。
当然,分页会影响性能,但这可能是使用.NET或Java等框架的GC可以带来优势的情况之一,因为GC是由知道分页发生的人编写的。结构中仍然有优势使其更有可能在同一页面上具有相关元素(支持通过指针追逐集合的数组支持集合)。这在CPU高速缓存方面也具有优势。 (大型数组仍然是导致GC崩溃的最佳方法之一,但是由于GC非常擅长这样做,所以它仍然会胜过处理相同问题的许多其他方法)。
使用C ++几乎可以肯定,因为我们通常在操作系统的内存管理级别编码 - 数组位于连续的虚拟空间(无论是堆还是堆栈),而不是连续的物理空间。在C或C ++中,可以在低于该值的级别进行编码,但这通常只能由实际编写内存管理代码的人来完成。