Reputation: 9424
I'm developing a simple game in Java just to use as a toy program to teach some techniques for my students, but I'm having some problem. My game uses two ArrayList that are iterated lots of times. These lists contains the projectiles that a ship fire and the targets that these projectiles can destroy. I need to constantly verify the colision between each projectile with each target in the screen among other things related to iterating throught these lists. I noted that while my program was running, the performance of it started to get worse and worse, so I started to profile the project (I'm using NetBeans profiler) to find problems.
One thing that I find is that using the for each of Java to iterate throught the lists (implies calling iterator() method inplicitly) a lot of memory started to be used and not released.
I wrote the code below to test this. When I profile it, the ArrayList$itr method memory consumption starts to grow and grow. The list has a fixed size, so I don't understand why the memory continues to grow, since I have the same data structure.
Take a look in the code:
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;
}
}
}
}
If you run the code and keep track of ArrayList$itr you will see that its memory consumption grows a lot during the execution time. In my game, this consumption is enormous (more than 200 MB and keeps growing). Using a regular for, this does not occur.
I would like to know if this behavior is correct, because for me its very strange.
Upvotes: 3
Views: 2540
Reputation: 2427
per your sample code, the behaviour is incorrect, but expected.
It needs to be very careful while using for-each loop instead of simple for-loop. If the code piece fulfills the following 3 conditions at the same time, it would probably leads to memory-leak:
while(true){...}
)Apparently, your toy program has fulfilled above 3 conditions at the same time.
Memory-leak occurs in above program because the following similar method will be called on the collection (e.g. in ArrayList ) inside the for-each loop before iterating ( next()
) or before checking whether could iterate over ( hasNext()
) the collection:
@NotNull public Iterator<E> iterator() {
return new Itr();
}
While the for-each loop is inside an infinite loop (i.e. while(true) ), which means infinite number of Itr
instance will be created. Finally, memory-leak is expected.
Upvotes: 1
Reputation: 2504
I tried to confirm Louis Wasserman's explanation experimentally, and indeed, I did not observe any memory leak in the code above (with JDK 1.8.60). What I did was:
Upvotes: 1