【问题标题】:Genetic Algorithm's Thread Pool Running Out of Memory - why?遗传算法的线程池内存不足 - 为什么?
【发布时间】:2012-06-19 03:41:12
【问题描述】:

所以我正在研究并行化遗传算法(用 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();
    }
}

在这一点上,我不知道是什么原因造成的。有谁知道为什么会发生这种情况以及我该如何解决?

附:我知道我可以尝试使用更多内存运行 JVM,但这仍然不能解释错误的特殊时间。考虑到我在一台机器上编写这个程序并最终将它移动到另一台机器上,我更愿意了解错误背后的原因,而不是以相对蛮力的方式修复它。

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

【问题讨论】:

  • “我现在的一个理论是 Java 没有进行正确的垃圾收集”——或者更有可能你的代码中有错误......
  • 人口规模每次迭代增加 10。内存使用量如何取决于人口规模?如果您正在考虑种群成员之间的交互,这可能会导致每一代的内存需求呈指数级增长。您可能只需要更多内存。说起来,你给多少钱?默认的-Xmx 非常小。
  • 看看这个帖子:stackoverflow.com/q/10742634/1140748也许你也有同样的问题。
  • @JimGarrison 执行器初始化为一个最大大小等于总体大小的固定线程池——这意味着每个人最多有一个线程。我知道这是很多,故意的 - 我试图看看算法的局限性是什么。奇怪的是,正如我所说,Trial 10 的第 3 轮和第 4 轮的线程池大小没有变化,所以我真的不认为这是导致错误的原因 - 否则它应该会更快出现,不是吗?
  • 奇怪的是ThreadPoolExecutor(尤其是newCachedThreadPool() 变体,它在需要时分配无限的最大线程数。)没有解决这个问题。理想情况下,Executor 将允许您提交几乎无限多的 Runnables 以供执行,并且分配的线程数不超过 JVM、OS 等支持的线程数。System 也没有方法 getRecommendedMaximumThreads()。

标签: java multithreading memory threadpool genetic-algorithm


【解决方案1】:

使用固定大小的线程池创建线程时内存不足听起来很奇怪。我怀疑以下情况之一:

  • 您的线程池实际上不是固定大小的;即你弄错了池创建参数。
  • 您的代码正在其他地方创建线程;例如通过显式调用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 for “强制 GC 运行也无济于事”。每当我在应用程序代码中看到 System.gc() 时,我都会感到毛骨悚然……
  • @thkala 我完全理解,而且我以前从未真正使用过它。我只是在这种情况下把它拿出来作为测试它是否是 Java 正确处理旧线程的问题。
【解决方案2】:

我将把它作为“答案”,因为会有很多 cmets。

我认为你想要的是 ThreadPoolExecutor。

实际上,我认为您可能会发现只需回到基础并启动一堆 Thread 实例并反复使用join 方法来找出它们何时都完成更简单。适当的线程池可以防止您在 2 核机器上同时运行 100 个线程,但我从经验中知道 Java 可以保持 1000 个线程直接运行而无需池。 (按照我的编码方式,大多数线程都在等待锁定并相互交谈,它们并非都完全耗尽。但是其中很多完全耗尽并且它们没有阻塞 CPU。)无论如何,让所有线程运行,然后然后尝试某种池。

Java 现在提供了各种类来使多线程处理变得更容易和更好,但它们的实际作用并不总是很清楚,尤其是当您尝试使程序运行而不是编写硕士论文时。

【讨论】:

  • 单独调用一堆线程会产生每一代创建和销毁这些线程的成本 - 线程池允许我运行我的算法,例如 500 代,同时仍然只使用相同的线程一遍又一遍,从长远来看,这将节省相当多的时间。但实际上,我已经切换到一个 ExecutorService(从一个普通的 Executor),现在它似乎工作正常。不过,我仍然不知道为什么 Java 没有销毁旧线程。
猜你喜欢
  • 1970-01-01
  • 2011-04-18
  • 2016-02-16
  • 1970-01-01
  • 2012-07-07
  • 2011-11-24
  • 1970-01-01
  • 1970-01-01
  • 2012-06-02
相关资源
最近更新 更多