因此,我正在研究并行化遗传算法(用 Java 编码),并且我决定使用 Executor 来管理我群体中个体的适应性测试的异步执行。我这样做是因为这意味着我可以创建一个具有固定线程池大小的执行程序,并且只需在每一代都重用这些线程,而不是每一代都创建新线程。
现在,我已经运行了一组测试来监控我的 GA 在人口规模不断增长的情况下的性能,但我遇到了障碍。执行以下代码:
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);
}
}
我收到以下错误:
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
令我感到困惑的是,这发生在第 10 轮第 4 轮 - 这意味着它能够毫无问题地运行第 10 轮的前三轮。由于运行第 4 轮时应该没有区别(特别是,第 4 轮不需要比试验 10 的第 1-3 轮更多的线程),我不认为它会有任何问题。但确实如此。
我现在的一个理论是 Java 没有进行正确的垃圾收集——我的意思是,出于某种原因,它没有清除旧的未使用线程,这就是为什么它在如此特殊的时刻内存不足的原因。认为就是这样,我尝试在循环内声明和分配 P ,而不是仅仅分配它。那没有效果。我还尝试P = null; System.gc();
在循环末尾添加以尝试在创建新线程池之前强制进行垃圾收集。同样,它没有任何区别。
以下是处理执行程序的相关代码行:
在人口()中:executor = Executors.newFixedThreadPool(popSize);
在 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.");
}
(我正在使用 CountDownLatch 来等待所有线程的执行完成——我已经在并行化时通过将每个个体的适应度测试放入他们自己的线程中来实现它,而不是通过执行程序使用线程池。与 ExecutorService 的 invokeAll() 方法相比,闩锁似乎更适合我的个人实现。)
Individual.run() 的代码:
public void run(){
try{
findFitness();
}catch (Exception e){
System.out.println("Error in Individual.run(): " + e.getMessage());
}finally{
stopLatch.countDown();
}
}
在这一点上,我不知道是什么原因造成的。有谁知道为什么会发生这种情况以及我该如何解决?
PS我知道我可以尝试使用更多内存运行JVM,但这仍然不能解释错误的特殊时间。考虑到我在一台机器上编写这个程序并最终将它移动到另一台机器上,我更愿意了解错误背后的原因,而不是以相对蛮力的方式修复它。
更新:通过并再次运行试验,这次通过 JConsole 观察线程,我可以确认执行程序正在创建大小合适的线程池。然而,线程池并没有被破坏——每一轮测试(即每次通过计算 j 的 for 循环),都会产生一个新的线程池,但旧的线程池仍然存在。为什么会发生这种情况?