Java ArrayList Iterator中可能的内存泄漏

时间:2015-09-23 23:40:40

标签: java arraylist memory-leaks

我正在开发一个简单的Java游戏,只是用作玩具程序来为我的学生教授一些技巧,但我遇到了一些问题。我的游戏使用了两次迭代很多次的ArrayList。这些清单包含船舶射击的射弹和这些射弹可以摧毁的目标。我需要不断验证每个射弹与屏幕中每个目标之间的切割以及与迭代这些列表相关的其他事项。我注意到,当我的程序运行时,它的性能开始变得越来越差,所以我开始描述项目(我使用NetBeans探查器)来查找问题。

我发现的一件事是,使用每个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,这不会发生。

我想知道这种行为是否正确,因为对我来说这很奇怪。

2 个答案:

答案 0 :(得分:0)

我试图通过实验确认Louis Wasserman的解释,事实上,我没有在上面的代码中观察到任何内存泄漏(使用JDK 1.8.60)。我做的是:

  • 将main方法的全部内容包装到while(true){...}循环中,以便列表被填充,迭代并无限制地清除。
  • 在jvisualvm中观看内存,不时强制GC。
  • 内存有时会增加到100MB左右,但在GC之后它总是下降到接近零。

答案 1 :(得分:0)

按照您的示例代码,此行为是不正确的,但是是预期的。

在使用for-each循环而不是简单的for循环时,需要非常小心。如果代码段同时满足以下三个条件,则可能导致内存泄漏:

  1. 使用for-each循环代替简单的for-loop
  2. for-each循环嵌套在无限循环内(例如while(true){...}
  3. for-each循环正在遍历集合(例如 ArrayList )而不是数组

显然,您的玩具程序同时满足了上述3个条件。

在上面的程序中发生内存泄漏,因为在迭代(next()之前或在检查是否可以迭代({之前,)在for-each循环内的集合(例如ArrayList)上调用以下类似方法{1}})集合:

hasNext()

尽管for-each循环位于无限循环内(即while(true)),这意味着将创建无限数量的@NotNull public Iterator<E> iterator() { return new Itr(); } 实例。最后,可能会出现内存泄漏。