So I'm working on parallelizing a genetic algorithm (coded in Java), and I've decided to use an Executor to manage the asynchronous execution of the fitness tests for the individuals in my population. I did this because it means I can create an executor with a fixed thread pool size and simply reuse those threads every generation as opposed to creating new threads every single generation.
Now, I have a set of tests I've been running to monitor the performance of my GA with growing population sizes, and I've run into a snag. Executing the following code:
for(i=1;i<=11; i++){
PopulationSize = 10*i;
for(j=0;j<10;j++){
startTime = System.nanoTime();
P = new Population(PopulationSize, crossOverProbability, mutationProbability, conGens);
while(P.generation()<10){
P.breedNewPop();
}
endTime = System.nanoTime();
time = (endTime - startTime) * Math.pow(10, -9);
System.out.println("Done Trial " + i + ", Round " + j);
}
}
I get the following error :
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
The confusing thing to me is that this is happening at Trial 10, Round 4 - meaning it was able to run the first three rounds of Trial 10 without a problem. Since there should be no difference when running Round 4 (particularly, Round 4 doesn't call for any more threads than Rounds 1-3 of Trial 10), I wouldn't expect that it would have any issue. But it does.
The one theory I have right now is that Java is not doing proper garbage collection - what I mean is that it's, for some reason, not clearing out the old unused threads, and that's why it runs out of memory at such a peculiar moment. Thinking this was it, I tried both declaring and assigning P inside the loop, instead of just assigning it. That had no effect. I also tried adding P = null; System.gc();
at the end of the loop to try and force garbage collection before the new thread pool was made. Again, it made no difference.
Here are the relevant lines of code dealing with the executor:
In Population(): executor = Executors.newFixedThreadPool(popSize);
In Population.findFitness():
for(int i=0; i<individuals.length; i++){
executor.execute(individuals[i]);
}try {
cdl.await();
} catch (InterruptedException e) {
System.out.println("Error: Thread interrupted.");
}
(I'm using a CountDownLatch to wait for the execution of all threads to be completed - I already had it implemented from when I was parallelizing by putting each Individual's fitness tests into their own threads, instead of using a thread pool through the executor. The latch also seemed like it would fit better with my implementation of Individual than something like the invokeAll() method of an ExecutorService.)
The code for Individual.run():
public void run(){
try{
findFitness();
}catch (Exception e){
System.out.println("Error in Individual.run(): " + e.getMessage());
}finally{
stopLatch.countDown();
}
}
At this point I'm at a loss as to what could be causing it. Does anyone have any ideas why this would be happening and how I could fix it?
P.S. I know I could try running the JVM with more memory, but that still doesn't explain the peculiar timing of the error. Considering that I'm programming this program on one machine and will eventually move it to another machine, I'd prefer to understand the reasons behind the error instead of fixing it in a relatively brute force manner.
UPDATE: Having gone through and run the trials again, this time watching the threads through JConsole, I can confirm that the executor is creating thread pools that are the right size just fine. However, the thread pools are NOT being destroyed - every Round of tests (ie. every time through the for loop that counts j), a new thread pool is produced but the old one remains. Why would this happen?