Java for循环是OutOfMemoryError的原因吗?(月食)
您好,我正在编写一个程序,将字符串解析为单个组件,但当我尝试测试它时,会出现内存不足错误。我觉得我的for/while循环是无限的,但我似乎找不到原因Java for循环是OutOfMemoryError的原因吗?(月食),java,eclipse,memory-leaks,out-of-memory,Java,Eclipse,Memory Leaks,Out Of Memory,您好,我正在编写一个程序,将字符串解析为单个组件,但当我尝试测试它时,会出现内存不足错误。我觉得我的for/while循环是无限的,但我似乎找不到原因 //for loop to loop through char of string for(int i=0; i<expressionString.length(); i++) { //cast char into ascii int int ascii = (int) charAt(i);
//for loop to loop through char of string
for(int i=0; i<expressionString.length(); i++) {
//cast char into ascii int
int ascii = (int) charAt(i);
//appending to token if one of singly operator symbols: *,/,(,),[,]
if(ascii == 40 || ascii == 41 || ascii == 42 || ascii == 47 || ascii == 91 || ascii == 93){
token.append((char) ascii);
tokenList.add(token.toString());
} //append if +, -
else if(ascii == 43 || ascii == 45) {
token.append((char) ascii);
//check next char if + or /, if so append to token again
int nextChar = (char) charAt(i+1);
if(nextChar == 43 || nextChar == 45) {
token.append((char) nextChar);
}
tokenList.add(token.toString());
} //appending to token if it's a num
else if ( ascii >= 48 || ascii <=57) {
token.append((char) ascii);
//check if next char is a num
while ((int) charAt(i+1) >= 48 || (int) charAt(i+1) <= 57) {
//increment i in for loop to check
i++;
token.append((int) charAt(i));
}
tokenList.add(token.toString());
}
//
}
//用于通过字符串的字符进行循环
对于(inti=0;i,这里是您在该循环中所做工作的简化版本
public class Main {
public static void main(String[] args) {
String str = "ABCDE";
StringBuilder sb = new StringBuilder();
List<String> list = new ArrayList<>();
for (char c : str.toCharArray()) {
sb.append(c);
list.add(sb.toString()); // <-- Problem! This adds the *entire* contents of the StringBuilder as a new String to the list.
}
System.out.println(list);
}
}
这是因为每次我们将char
附加到StringBuilder
,然后将StringBuilder
的全部内容作为新的String
添加到ArrayList
现在假设我们用长度为1000000
的字符串替换“ABCDE”
,例如,我们将第一行更改为
String str = Stream.generate(() -> "A").limit(1000000).collect(Collectors.joining()); // String of length 1000000
我们现在正在尝试创建1000000个字符串
对象,其长度从1
到1000000
,结果可以预测
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOfRange(Arrays.java:3664)
at java.lang.String.<init>(String.java:207)
at java.lang.StringBuilder.toString(StringBuilder.java:407)
at my_package.Main.main(Main.java:17)
线程“main”java.lang.OutOfMemoryError中的异常:java堆空间
位于java.util.Arrays.copyOfRange(Arrays.java:3664)
位于java.lang.String。(String.java:207)
位于java.lang.StringBuilder.toString(StringBuilder.java:407)
在my_package.Main.Main(Main.java:17)中
如何修复它?这取决于您正在尝试做什么(我们没有所有的上下文),但我怀疑您不需要一个StringBuilder
和一个列表
这里是您在该循环中所做工作的简化版本
public class Main {
public static void main(String[] args) {
String str = "ABCDE";
StringBuilder sb = new StringBuilder();
List<String> list = new ArrayList<>();
for (char c : str.toCharArray()) {
sb.append(c);
list.add(sb.toString()); // <-- Problem! This adds the *entire* contents of the StringBuilder as a new String to the list.
}
System.out.println(list);
}
}
这是因为每次我们将char
附加到StringBuilder
,然后将StringBuilder
的全部内容作为新的String
添加到ArrayList
现在假设我们用长度为1000000
的字符串替换“ABCDE”
,例如,我们将第一行更改为
String str = Stream.generate(() -> "A").limit(1000000).collect(Collectors.joining()); // String of length 1000000
我们现在正在尝试创建1000000个字符串
对象,其长度从1
到1000000
,结果可以预测
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOfRange(Arrays.java:3664)
at java.lang.String.<init>(String.java:207)
at java.lang.StringBuilder.toString(StringBuilder.java:407)
at my_package.Main.main(Main.java:17)
线程“main”java.lang.OutOfMemoryError中的异常:java堆空间
位于java.util.Arrays.copyOfRange(Arrays.java:3664)
位于java.lang.String。(String.java:207)
位于java.lang.StringBuilder.toString(StringBuilder.java:407)
在my_package.Main.Main(Main.java:17)中
如何修复它?这取决于您正在尝试做什么(我们没有所有的上下文),但我怀疑您不需要一个StringBuilder
和一个列表,正如我在评论中所指出的,您在StringBuilder
中添加任何内容都是可疑的
StringBuilder
只是一个围绕char[]
的包装器,它会在必要时自动调整大小,以适应您尝试附加的新文本。您可以在堆栈跟踪中看到,在这些自动调整大小的过程中,OOM正在发生
这个问题的一个解决方案是,首先分配一个足够大的缓冲区,然后在StringBuilder
中添加更多文本之前,不需要进行大小调整:
StringBuilder token = new StringBuilder(MAXIMUM_EXPECTED_SIZE);
这样做的问题是,可能很难确定最大预期大小
;此外,在大多数情况下,您可能会浪费大量内存,而在缓冲区中添加的文本量与预期大小相差甚远
在将文本传输到tokenList
后,您似乎实际上不想将其保留在token
中。您可以使用以下方法将其从缓冲区中明确删除:
token.delete(0, token.length());
// or
token.setLength(0);
(实际上,这并不删除数据,它只允许后续的附件覆盖数据)
但这仍然是浪费:您根本不需要StringBuilder
考虑一下如何处理这些数字:
if ( ascii >= 48 || ascii <=57) {
token.append((char) ascii);
//check if next char is a num
while ((int) charAt(i+1) >= 48 && (int) charAt(i+1) <= 57) {
// ^^ NB
//increment i in for loop to check
i++;
token.append((int) charAt(i));
}
tokenList.add(token.toString());
}
您也可以对其他附加令牌执行类似操作。这只是删除了“中间人”对于StringBuilder
,这显然避免了重新分配其内部缓冲区的问题。正如我在评论中指出的,您添加到StringBuilder
而从未从中删除任何内容的事实是可疑的
StringBuilder
只是一个围绕char[]
的包装器,它会在必要时自动调整大小,以适应您尝试附加的新文本。您可以在堆栈跟踪中看到,在这些自动调整大小的过程中,OOM正在发生
这个问题的一个解决方案是,首先分配一个足够大的缓冲区,然后在StringBuilder
中添加更多文本之前,不需要进行大小调整:
StringBuilder token = new StringBuilder(MAXIMUM_EXPECTED_SIZE);
这样做的问题是,可能很难确定最大预期大小
;此外,在大多数情况下,您可能会浪费大量内存,而在缓冲区中添加的文本量与预期大小相差甚远
在将文本传输到tokenList
后,您似乎实际上不想将其保留在token
中。您可以使用以下方法将其从缓冲区中明确删除:
token.delete(0, token.length());
// or
token.setLength(0);
(实际上,这并不删除数据,它只允许后续的附件覆盖数据)
但这仍然是浪费:您根本不需要StringBuilder
考虑一下如何处理这些数字:
if ( ascii >= 48 || ascii <=57) {
token.append((char) ascii);
//check if next char is a num
while ((int) charAt(i+1) >= 48 && (int) charAt(i+1) <= 57) {
// ^^ NB
//increment i in for loop to check
i++;
token.append((int) charAt(i));
}
tokenList.add(token.toString());
}
您也可以对其他附加令牌执行类似操作。这只是删除了“中间人”这显然避免了重新分配内部缓冲区的问题。第51行是什么?这似乎是发生异常的地方。另外:我认为这里需要一个&
,而不是|
(ascii>=48 | | ascii除外:您不需要对“*,/,(,,[,]”进行注释如果您只是在条件中使用了ascii=='*'
etc(或“*/()[]”。indexOf((char)ascii)>=0
)。如果@MFisherKDX的提示不能帮助提供整个类,请。您是forev