我正在用Java开发一个简单的游戏,只是为了作为一个玩具程序来教我的学生一些技巧,但是我遇到了一些问题。我的游戏使用两个ArrayList,多次迭代。这些清单包含船只发射的射弹和这些射弹可以摧毁的目标。我需要不断地验证每个弹丸与屏幕上的每个目标之间的合并,以及与迭代这些列表相关的其他事情。我注意到,当我的程序运行时,它的性能开始变得越来越差,所以我开始分析这个项目(我正在使用NetBeans分析器)来发现问题。
我发现的一件事是,使用for每个Java遍历列表(暗指隐式调用iterator()方法)开始使用大量内存,而不是释放。
我编写了下面的代码来测试这个。当我分析它时,ArrayList$itr方法的内存消耗开始增长。列表有固定的大小,所以我不明白为什么内存会继续增长,因为我有相同的数据结构。
看一看代码:
import java.util.ArrayList;
import java.util.List;
public class MemoryLeak {
public static void main( String[] args ) {
List<String> dummyData = new ArrayList<>();
long quantity = 1000;
long iterationTimeWithData = 60000;
long iterationTimeEmpty = 10000;
System.out.println( "adding data" );
for ( int i = 0; i < quantity; i++ ) {
dummyData.add( String.valueOf( Math.random() ) );
}
System.out.printf( "iterating through the list for %d seconds\n", iterationTimeWithData/1000 );
long startTime = System.currentTimeMillis();
while ( true ) {
for ( String d : dummyData ) {}
if ( System.currentTimeMillis() - startTime > iterationTimeWithData ) {
break;
}
}
System.out.println( "clear the list" );
dummyData.clear();
System.out.printf( "iterating through the empty list for %d seconds\n", iterationTimeEmpty/1000 );
startTime = System.currentTimeMillis();
while ( true ) {
for ( String d : dummyData ) {}
if ( System.currentTimeMillis() - startTime > iterationTimeEmpty ) {
break;
}
}
}
}
如果您运行代码并跟踪ArrayList$itr,您将看到它的内存消耗在执行期间增长了很多。在我的游戏中,这种消费是巨大的(超过200 MB,并不断增长)。使用正则for时,不会发生这种情况。
我想知道这种行为是否正确,因为对我来说很奇怪。
发布于 2015-11-11 11:37:17
我试图在实验上证实Louis的解释,实际上,我没有在上面的代码中观察到任何内存泄漏(使用JDK 1.8.60)。我所做的是:
发布于 2020-12-11 02:22:18
根据您的示例代码,这种行为是不正确的,但是是预期的。
在使用for-每个循环而不是简单的for-循环时,需要非常小心。如果代码同时满足以下3种条件,可能会导致内存泄漏:
while(true){...}
)。很明显,你的玩具程序同时满足了以上三个条件。
在上面的程序中会发生内存泄漏,因为在迭代( ArrayList )之前或在检查是否可以迭代集合( hasNext()
)之前,会在for-each循环中调用以下类似的方法(例如,在hasNext()
中):
@NotNull public Iterator<E> iterator() {
return new Itr();
}
当for-each循环位于无限循环(即while(true) )内时,这意味着将创建无限数量的Itr
实例。最后,内存泄漏是预期的。
https://stackoverflow.com/questions/32751181
复制