当前位置: 首页 > 知识库问答 >
问题:

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

邓元白
2023-03-14

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

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

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

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

现在发生了什么?

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

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

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

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

引用不会占用太多空间。但无论如何,一些空间被利用了。当阵列越来越大时,可能会出现问题。我们也不能忘记,我们还有另一种东西需要使用内存空间。

共有3个答案

吕翰飞
2023-03-14

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

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

因此,如果您的内存限制为10 MB,并且阵列占用2 MB,大小为3 MB,字符串占用6 MB,则将抛出OOM,即使在执行此操作后,您的内存只有3 6=9 MB。如果您想使用一个巨大的数组运行时接近内存限制,一种避免这种情况的方法是将数组的大小调整为开始时的最大大小,这样就不需要调整数组的大小。

归浩博
2023-03-14

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

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

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

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

秦安宁
2023-03-14

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

OutOfMemoryError

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

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

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

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

 类似资料:
  • 问题内容: 据我所知,当我们创建一个时: JVM为此保留了内存的连续部分。当我们将新元素添加到列表中时,当元素数量达到75%时,它将保留新的连续内存部分并复制所有元素。 我们的名单越来越大。我们正在添加新对象,并且必须再次重建列表。 现在会发生什么? JVM正在寻找连续的内存段,但是找不到足够的空间。 垃圾收集器可以尝试删除一些未使用的引用和碎片整理内存。如果JVM在此过程之后无法为列表的新实例保

  • 通过查看shmget()的手动页面,我了解到shmget()调用在内存中分配了#个页面,这些页面可以在进程之间共享。 它是否要创建内核内存页,并将其映射到进程的本地地址空间?还是为该段保留了相同的进程内存页,并将为其他附加进程共享相同的内存页? 调用shmget()时,内核将保留一定数量的段/页。 调用shmat()时,保留的段映射到进程的地址空间/页。 当一个新进程附加到同一段时,前面创建的内核

  • 问题内容: ArrayList在内部使用什么数据结构? 问题答案: 内部使用。 在向中添加项目时,列表会检查后备阵列是否还有剩余空间。如果有空间,则将新项目添加到下一个空白处。如果没有空间,则会创建一个更大的新阵列,并将旧阵列复制到新阵列中。 现在,还有更多空间,新元素将添加到下一个空白空间。 由于人们真的很喜欢源代码: 直接跳出JDK。

  • 问题内容: 我正在通过Sequelize寻找查询,但似乎找不到任何文档。 问题答案: 问题:https : //github.com/sequelize/sequelize/issues/348 我使用sequelize@2.0.0-dev9

  • 我试图弄清楚实际上是如何在内存级别上工作的。在linux手册页中: 我知道实现细节可能留给操作系统之类的东西。但我想知道,总的来说,这是如何实现的。

  • 问题内容: 我需要存储大量信息,例如在Java List中存储“名称”。项目的数量可以更改(或者简而言之,我无法预定义大小)。我认为从内存分配的角度来看,LinkedList比ArrayList更好,对于ArrayList,一旦达到最大大小,内存分配将自动加倍,因此总有可能分配比需要什么。 我从这里的其他文章中了解到,存储在LinkedList中的各个元素比ArrayList占用更多的空间,因为L