gpt4 book ai didi

java - 为什么存储长字符串会导致 OOM 错误,但将其分解为短字符串列表却不会?

转载 作者:搜寻专家 更新时间:2023-10-30 21:29:43 26 4
gpt4 key购买 nike

我有一个 Java 程序使用 StringBuilder 从输入流构建字符串,最终当字符串太长时导致内存不足错误。我尝试将它分解成更短的字符串并将它们存储在 ArrayList 中,这避免了 OOM,即使我试图存储相同数量的数据。这是为什么?

我怀疑对于一个非常长的字符串,计算机必须在内存中为它找到一个连续的位置,但是对于 ArrayList,它可以使用内存中多个较小的位置。我知道 Java 中的内存可能很棘手,所以这个问题可能没有直接的答案,但希望有人能让我走上正轨。谢谢!

最佳答案

基本上,你是对的。

StringBuilder(更准确地说,AbstractStringBuilder)使用 char[] 来存储字符串表示(尽管通常是 String 不是 char[])。而 Java 做 not guarantee一个数组确实存储在连续的内存中,很可能是这样。因此,每当将字符串附加到底层数组时,都会分配一个新数组,如果它太大,则会抛出 OutOfMemoryError

确实,执行代码

StringBuilder b = new StringBuilder();
for (int i = 0; i < 7 * Math.pow(10, 8); i++)
b.append("a"); // line 11

抛出异常:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3332)
at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448)
at java.lang.StringBuilder.append(StringBuilder.java:136)
at test1.Main.main(Main.java:11)

当在 Arrays.copyOf 中到达第 3332 行 char[] copy = new char[newLength]; 时,抛出异常,因为没有足够的内存用于大小为 newLength 的数组。

另请注意与错误一起给出的消息:“Java 堆空间”。这意味着无法在 Java 堆中分配对象(在本例中为数组)。 (编辑:此错误还有另一个可能的原因,请参阅 Marco13's answer)。

2.5.3. Heap

The Java Virtual Machine has a heap that is shared among all Java Virtual Machine threads. The heap is the run-time data area from which memory for all class instances and arrays is allocated.

... The memory for the heap does not need to be contiguous.

A Java Virtual Machine implementation may provide the programmer or the user control over the initial size of the heap, as well as, if the heap can be dynamically expanded or contracted, control over the maximum and minimum heap size.

The following exceptional condition is associated with the heap:

  • If a computation requires more heap than can be made available by the automatic storage management system, the Java Virtual Machine throws an OutOfMemoryError.

将数组分成总大小相同的较小数组可避免 OOME,因为每个数组都可以单独存储在较小的连续区域中。当然,您必须通过从每个数组指向下一个数组来为此“付出”代价。

将上面的代码与这一段进行比较:

static StringBuilder b1 = new StringBuilder();
static StringBuilder b2 = new StringBuilder();
...
static StringBuilder b10 = new StringBuilder();

public static void main(String[] args) {
for (int i = 0; i < Math.pow(10, 8); i++)
b1.append("a");
System.out.println(b1.length());
// ...
for (int i = 0; i < Math.pow(10, 8); i++)
b10.append("a");
System.out.println(b10.length());
}

输出是

100000000
100000000
100000000
100000000
100000000
100000000
100000000
100000000

然后抛出 OOME。

虽然第一个程序不能分配超过 7 * Math.pow(10, 8) 个数组单元,但这个程序总和至少为 8 * Math.pow(10, 8)

请注意,堆的大小可以通过 VM 初始化参数进行更改,因此抛出 OOME 的大小在系统之间不是恒定的。

关于java - 为什么存储长字符串会导致 OOM 错误,但将其分解为短字符串列表却不会?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45406035/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com