4

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

我正在处理的文本文件有 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();
4

2 回答 2

2

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

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

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

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

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

在我看来,您需要分析您的内存使用情况。

于 2013-04-17T16:48:02.450 回答
0

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

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

我的猜测是线程没有退出,而是以某种方式卡住了。我建议进行线程转储(kill -QUIT pid在 *nix 下或通过 using jstack)并查看线程在哪里。这将帮助您确定它们是否被卡在某个地方。

我怀疑您的程序开始时运行了 24 个线程,但随着时间的推移,您会丢失一个,然后再丢失另一个。尽管最后似乎性能急剧下降,但我想知道程序是否从一开始就变得越来越慢。

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

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

希望这可以帮助。

于 2013-04-18T04:16:40.487 回答