ArrayList 的内存分配如何工作?

2022-09-01 05:07:25

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

ArrayList<String> list = new ArrayList<String>(SIZE);

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

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

现在会发生什么?

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

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

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

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

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


答案 1

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

OutOfMemoryError

就是这样。实际上,JVM 内存分配只有两种可能的结果:

  1. 为应用程序提供请求的内存量。
  2. JVM 抛出 OutOfMemoryError.

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

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


答案 2

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

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

因此,在调整数组大小的任何时候,JVM 都需要找到一个连续内存来存储新大小数组的引用。因此,如果要将数组扩展到1000个元素的大小,并且每个引用都是一个字节,JVm将尝试找到一个1000 * 1KB的连续内存,即1 MB。如果找到这个内存,它会做一个引用的副本,并为GC标记旧的连续内存,每当GC下次运行时 如果它找不到内存,它会尝试做一个GC,如果仍然没有找到连续内存,它会抛出一个内存不足异常

这是 ArrayList 中执行大小调整的代码。http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/ArrayList.java#ArrayList.ensureCapacity%28int%29