ArrayList的内存分配如何工作?

ruh*_*gry 31 java memory arraylist

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

ArrayList<String> list = new ArrayList<String>(SIZE);
Run Code Online (Sandbox Code Playgroud)

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

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

现在发生了什么?

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

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

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

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

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

Den*_*gin 36

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

OutOfMemoryError
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

  • `OutOfMemoryError`不会导致JVM退出.它只是`java.lang.Error`的子类型.它将展开它所引发的线程的堆栈,直到它被捕获. (12认同)
  • 来自Java文档:"错误是*Throwable*的子类,表示合理的应用程序**不应该试图捕获的严重问题**.大多数此类错误都是异常情况." (4认同)
  • 我同意最好将"错误"视为致命,但应用程序可能会在退出之前尝试响应错误(日志记录,尽力而为清理).此外,在多线程程序中,未被捕获的错误可能不会导致JVM自行退出,它只会杀死它所引发的线程. (3认同)
  • @Dev你是对的.但它被称为*Error*主要是因为没有太多理由去捕获它 - 在OutOfMemoryError之后恢复应用程序几乎不可能. (2认同)

Bis*_*_86 16

在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 memory异常

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


Rus*_*ser 7

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

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

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