Java线程在处理结束时变慢

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

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

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

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

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

编辑

在我收到的评论之后,我在这里粘贴了一个堆栈跟踪。 如您所见,它看起来好像没有任何线程阻塞。 此外,在分析时,当事情变慢时,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(); 

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

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

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

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

我还要看看你的程序是否应该使用StringBuilders构建太多的中间字符串。

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

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

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

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

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

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

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

希望这可以帮助。