ArrayList的内存分配如何工作?

据我所知,当我们创建一个ArrayList

 ArrayList list = new ArrayList(SIZE); 

JVM为它保留了连续的内存部分 。 当我们在列表中添加新元素时,当元素数量达到SIZE 75%时,它会保留一个新的,连续的内存部分并复制所有元素。

我们的名单越来越大。 我们正在添加新对象,并且必须再次重建列表。

现在发生了什么?

JVM正在寻找一个连续的内存段,但它找不到足够的空间。

垃圾收集器可以尝试删除一些未使用的引用和碎片整理内存。 如果JVM在此过程之后无法为新的列表实例保留空间,会发生什么?

它是否使用最大可能的段创建一个新的? 将抛出哪个Exception

我读了这个问题Java:ArrayList如何管理内存 ,其中一个答案是:

参考不会占用太多空间。 但无论如何,使用了一些空间。 当数组越来越大时,可能会出现问题。 我们也不能忘记我们还有另外一些使用内存空间的东西。

如果JVM无法分配请求的内存量,它将抛出

 OutOfMemoryError 

而已。 实际上JVM内存分配只有两种可能的结果:

  1. 给予应用程序请求的内存量。
  2. JVM抛出OutOfMemoryError。

没有中间选项,例如分配了一些内存量

它与ArrayList无关,这是一个JVM问题。 如果您询问ArrayList是否以某种方式以某种方式管理这种情况 – 那么答案是“不,它没有”。 它只是尝试分配它需要的内存量,让JVM考虑其余的内存。

在Java中,对对象的引用存储在连续的内存中。 实际对象可以保持不连续的方式。 因此,对于ex,您的数组可能有10个对象,JVM只需要为对象引用而不是对象保留内存。 因此,如果每个引用都采用字节(大约不是正确的值),但每个对象占用一个KB,并且您有一个包含10个元素的数组,JVm将尝试保留仅1 * 10 B即10 B的连续内存。对象可以驻留在10个不同的内存位置,总共10KB。 请记住,连续和非连续的内存空间都是分配给线程的内存。

当需要调整数组大小时,JVM试图找到一个较新长度的contiguos数组。 因此,如果要将数组的大小从10个调整为20个,它将尝试保留20 KB的连续空间(使用上面的示例)。 如果找到此空间,它将执行从旧数组到新数组的引用的副本。 如果找不到此空间,它将尝试执行GC。 如果它仍然没有找到空格,则会抛出OutofMemoryException。

因此,在调整数组大小的任何时候,JVM都需要找到contiguos内存来存储新大小数组的引用。 因此,如果您希望将数组扩展为大小为1000个元素,并且每个引用都是一个字节,则JVm将尝试查找1000 * 1KB的连续内存,即1 MB。 如果找到此内存,它将执行引用的副本,并为GC标记oldeer contiguos内存,每当GC下次运行时如果它无法找到内存,它将尝试执行GC,如果它仍然没有找到contiguos内存,它会抛出一个Out of memoryexception

这是ArrayList中用于resize的代码。 http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/ArrayList.java#ArrayList.ensureCapacity%28int%29

一旦没有足够的堆空间来分配新数组,这将抛出OutOfMemoryError

垃圾收集将始终在抛出此错误之前完成。 这将压缩内存并消除所有不再使用的较小尺寸的arrays。 但是没有办法解决旧数组,新数组和所有包含的对象需要同时存在于内存中以便将旧内容复制到新列表中的事实。

因此,如果您的内存限制为10 MB,并且arrays占用2 MB并且大小最大为3 MB,并且字符串占用6 MB,那么即使在此操作之后您将只有3 +,也会抛出OOM 6 = 9 MB内存。 避免这种情况的一种方法是,如果你想用一个巨大的数组运行非常接近内存限制,那就是将数组的大小调整为完整大小,以便它永远不需要resize。

我假设它将耗尽内存,因为在JVM可以扩展数组大小的情况下将没有空间可用。

我想要纠正的第一件事是,当我们在列表中添加新元素时,当元素数量达到SIZE的100%时 ,它会保留一个新的,连续的内存部分并复制所有元素。

ArrayList的新大小将是:

ArrayList的NewSize =(CurrentSize * 3/2)+ 1

但是从不推荐这种方式,如果我们知道需要存储多少对象,那么我们可以使用以下ArrayList的构造函数: –

ArrayList ar = new ArrayList(int initialCapacity);

如果我们的JVM无法在堆上为ArrayList指定足够的连续空间,那么在运行时我们会得到

  • 运行时错误:OutOfMemoryError