遗传算法线程池耗尽内存 - 为什么?

时间:2012-06-16 06:24:52

标签: java multithreading memory threadpool genetic-algorithm

所以我正致力于并行化遗传算法(用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轮的第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等待所有线程的执行完成 - 我已经通过将每个Individual的健身测试放入他们自己的线程来实现并行化了通过执行程序使用线程池。锁存器似乎更适合我的Individual实现,而不是像ExecutorService的invokeAll()方法。)

Individual.run()的代码:

public void run(){
    try{
        findFitness();
    }catch (Exception e){ 
        System.out.println("Error in Individual.run(): " + e.getMessage());
    }finally{
        stopLatch.countDown();
    }
}

此时我不知道是什么原因造成的。有没有人有任何想法为什么会发生这种情况以及如何解决它?

P.S。我知道我可以尝试运行具有更多内存的JVM,但这仍然无法解释错误的特殊时间。考虑到我在一台机器上编程这个程序并最终将它移动到另一台机器上,我更倾向于理解错误背后的原因,而不是以相对强力的方式修复它。

更新:经过并再次运行试验,这次通过JConsole观察线程,我可以确认执行程序正在创建大小合适的线程池。但是,线程池没有被销毁 - 每一轮测试(即每次通过for循环计算j),都会生成一个新的线程池,但旧的线程池仍然存在。为什么会这样?

2 个答案:

答案 0 :(得分:3)

创建具有固定大小的线程池的线程时内存不足听起来最奇怪。我怀疑其中一件事:

  • 您的线程池实际上不是固定大小的;即你的池创建参数错误。
  • 您的代码正在其他地方创建线程;例如通过明确调用new Thread().start()。这可能会出现在堆栈跟踪中。

另一种可能性是JVM外部的某些东西导致JVM无法分配线程堆栈。这些不在普通堆内存中分配,因此它不是-Xmx设置。它可能是默认的线程堆栈大小设置,也可能是外部资源限制...或者是计算机上的常规资源不足。


使用此异常消息:

Exception in thread "main" java.lang.OutOfMemoryError: 
     unable to create new native thread .

这显然不是GC检测到的正常“堆太满”类型的问题。失败的内存分配是对线程堆栈的非堆内存的请求。增加堆大小不会有帮助......甚至可能会使事情变得更糟。

强制GC运行也无济于事。即使问题是通过分配堆对象触发的,它也无济于事......因为JVM只会在运行GC后抛出一堆OOME。

答案 1 :(得分:0)

我要做一个&#34;回答&#34;因为有很多评论。

我认为你想要的是ThreadPoolExecutor。

实际上,我认为你可能会发现回到基础并启动一堆Thread实例并重复使用join方法以找出它们何时完成它们会更简单。一个合适的线程池将使你无法在2核机器上同时运行100个线程,但我从经验中知道Java可以保持1000个线程直接而不需要池。 (我编码的方式,大多数线程都在等待锁定并且彼此交谈,但他们并非全都没有运行。但是很多人 正在运行并且他们不会#39; t阻塞CPU。)无论如何,让所有线程进入,然后尝试某种池。

Java现在提供各种类,使多线程更容易和更好,但它并不总是真正清楚它们实际上做了什么,特别是当你试图使程序工作而不是写作时硕士论文。