Java线程在处理结束时变慢

时间:2013-04-17 15:45:44

标签: java multithreading lucene java.util.concurrent

我有一个Java程序,它接收一个包含文本文件列表的文本文件,并分别处理每一行。为了加快处理速度,我使用带有24个线程的FixedThreadPool的ExecutorService来使用线程。该机器有24个内核和48GB内存。

我正在处理的文本文件有250万行。我发现,对于前230万行左右,在CPU利用率很高的情况下运行良好。然而,超过某些点(大约在2.3行),性能退化,只使用了一个CPU,我的程序几乎停止了。

我调查了很多原因,确保关闭所有文件句柄,并增加提供给JVM的内存量。但是,无论我改变什么,性能总是会降低到最后。我甚至尝试过包含更少行的文本文件,并且在处理文件结束时性能再次降低。

除了标准的Java并发库之外,代码还使用Lucene库进行文本处理和分析。

当我没有对此代码进行处理时,性能是恒定的,并且不会在最后退化。我知道这是一个黑暗的镜头,很难描述发生了什么,但我想我会看到是否有人有任何想法可能会导致这种性能退化到底。

修改

在我收到的评论之后,我已经粘贴了一个堆栈跟踪here。如您所见,它似乎没有任何线程阻塞。此外,在分析时,当事情变慢时,GC不是100%。实际上,CPU和GC的利用率在大多数情况下都是0%,CPU会偶尔处理一些文件,然后再次停止。

执行线程的代码

 BufferedReader read = new BufferedReader(new FileReader(inputFile));
 ExecutorService executor = Executors.newFixedThreadPool(NTHREADS);
 String line;
 while ((line = read.readLine()) != null) { //index each line
     Runnable worker = new CharikarHashThreader(line, bits, minTokens);
     executor.execute(worker);
 }
 read.close();

2 个答案:

答案 0 :(得分:2)

这听起来很像垃圾收集/内存问题。

当垃圾收集运行时,它会暂停所有线程,以便GC线程可以执行其“可收集的垃圾”分析,而不会对其进行任何更改。当GC运行时,您将看到100%的正好1个线程,其他线程将停留在0%。

我会考虑添加一些Runtime.freeMemory()调用(或使用分析器)来查看在GC期间是否“发生停顿”。

我还尝试在你文件的前10k行运行你的程序,看看是否有用。

我还要查看你的程序在使用StringBuilders时是否构建了太多的中间字符串。

听起来我觉得你需要描述你的内存使用情况。

答案 1 :(得分:0)

我最初认为这也是GC问题,但我不太确定会提供以下信息。

  

我甚至尝试过包含更少行的文本文件,并且在处理文件结束时性能再次降低。

我的猜测是线程没有退出但是以某种方式卡住了。我建议在* nix或使用kill -QUIT pid下进行线程转储(jstack)并查看线程的位置。这将帮助您确定它们是否在某处堵塞。

我怀疑你的程序以24个线程运行开始,但随着时间的推移,你会失去一个然后又失去另一个。虽然看起来最终有一个戏剧性的性能下降但我想知道该程序从一开始就变得越来越慢。

  • 注意没有正确连接或IO超时的套接字。
  • 也许是某种阻塞线程的锁争用?
  • Lucene正在做的事情可能是导致争用或阻塞你的线程。正如@GPI所提到的,我会尝试评论Lucene调用并查看问题是否消失。同样,堆栈跟踪也会向您显示此信息。

确定线程阻塞的位置后,您需要解决锁定问题,为网络调用添加超时或以其他方式解决问题。

希望这有帮助。