Java ArrayList的内存分配是如何工作的?

Java ArrayList的内存分配是如何工作的?,java,memory,arraylist,Java,Memory,Arraylist,据我所知,当我们创建ArrayList时: ArrayList<String> list = new ArrayList<String>(SIZE); ArrayList list=新的ArrayList(大小); JVM为它预留了空间。当我们向列表中添加新元素时,当元素数量达到大小的75%时,它会保留一个新的、连续的内存部分并复制所有元素 我们的名单越来越大。我们正在添加新对象,必须再次重建列表 现在发生了什么 JVM正在寻找连续的内存段,但找不到足够的空间 垃圾收

据我所知,当我们创建
ArrayList
时:

ArrayList<String> list = new ArrayList<String>(SIZE);
ArrayList list=新的ArrayList(大小);
JVM为它预留了空间。当我们向列表中添加新元素时,当元素数量达到
大小的75%
时,它会保留一个新的、连续的内存部分并复制所有元素

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

现在发生了什么

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

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

它是否创建了一个新的,使用最大可能的段?将引发哪个
异常

我读了这个问题,其中一个答案是:

但无论如何,一些空间被利用了。当阵列变大时,可能会出现问题。我们也不能忘记,我们还有另一种东西,它利用了记忆空间


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

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

  • 应用程序将获得请求的内存量
  • JVM抛出OutOfMemoryError
  • 没有中间选项,比如分配了一些内存


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

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

    垃圾收集将始终在抛出此错误之前完成。这将压缩内存并消除所有不再使用的较小大小的阵列。但是,为了将旧内容复制到新列表中,旧数组、新数组和所有包含的对象都需要同时在内存中,这是无法回避的事实


    因此,如果内存限制为10 MB,数组占用2 MB,大小为3 MB,字符串占用6 MB,则将抛出OOM,即使在此操作之后,内存中只有3+6=9 MB。如果你想用一个巨大的数组运行到接近内存限制的地方,有一种方法可以避免这种情况,那就是首先将数组的大小调整到完全大小,这样它就不需要调整大小。

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

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

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

    这是ArrayList中用于调整大小的代码。

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

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

    ArrayList的新大小将为:

    ArrayList的新闻大小=(当前大小*3/2)+1

    但不建议这样做,如果我们知道需要存储多少对象,那么我们可以使用以下ArrayList构造函数:-

    ArrayList ar=新的ArrayList(int initialCapacity)

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

    • 运行时错误:OutOfMemoryError

    OutOfMemoryError
    不会导致JVM退出。它只是
    java.lang.Error
    的一个子类型。它将展开它所提升的线程堆栈,直到它被捕获为止。@Dev您是对的。但它之所以被称为错误,主要是因为没有太多的理由去捕捉它——在OutOfMemoryError之后恢复应用程序几乎是不可能的。从Java文档中可以看出:“错误是Throwable的一个子类,表示一个合理的应用程序不应该捕捉的严重问题。大多数这样的错误都是异常情况。”我同意将
    错误视为致命错误是最佳做法,但应用程序可能会尝试响应错误(日志记录、best-e)