ArrayList的内存分配如何工作?

时间:2014-04-23 13:10:47

标签: java memory arraylist

据我所知,当我们创建ArrayList

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

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

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

现在发生了什么?

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

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

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

我读了这个问题Java: How ArrayList manages memory,其中一个答案是:

  

Reference doesn't consume much space.但无论如何,使用了一些空间。当数组变大时,可能会出现问题。我们也不能忘记我们还有另外一些使用内存空间的东西。

5 个答案:

答案 0 :(得分:36)

如果JVM无法分配所请求的内存量,则会抛出

OutOfMemoryError

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

  1. 给予应用程序请求的内存量。
  2. JVM抛出OutOfMemoryError。
  3. 没有中间选项,例如分配了一些内存

    它与 ArrayList 无关,它是一个JVM问题。如果您询问 ArrayList 是否以某种方式以某种方式管理这种情况 - 那么答案是&#34;不,它不会。&#34;它只是尝试分配它所需的内存量,让JVM考虑其余内容。

答案 1 :(得分:14)

在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内存,则会抛出内存不足异常

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

答案 2 :(得分:7)

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

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

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

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

ArrayList的新大小将是:

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

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

ArrayList ar = new ArrayList(int initialCapacity);

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

  • 运行时错误:OutOfMemoryError