Java内存使用 / 线程池性能问题

4
这些显然需要仔细检查和代码的可用性,以彻底分析并给出良好的建议。然而,这并非总是可能的,我希望能够根据下面提供的信息为我提供好的提示。
我有一个服务器应用程序,它使用监听线程来监听传入的数据。传入的数据被解释成特定于应用程序的消息,这些消息随后引发事件。
在那一点上,我真的没有对如何处理事情有任何控制。
由于这是一个传统的应用程序,这些事件先前由同一个侦听器线程(主要是单线程应用程序)处理。事件被发送到黑盒子中,然后得到一个结果,应该写入磁盘。
为了提高吞吐量,我想使用线程池来处理这些事件。想法是侦听器线程可以每次创建事件时生成新任务,线程将负责执行黑盒子调用。最后,我有一个后台线程执行写入磁盘的操作。
只有使用以前的设置和后台编写器,一切正常,吞吐量比以前增加了约1.6倍。
但是,当我添加线程池时,性能会降低。起初,一切似乎都运行顺利,但是过了一段时间后,一切都变得非常缓慢,最后我得到了OutOfMemoryExceptions。奇怪的是,当我每次向池中添加任务时打印活动线程数(以及有关排队多少个任务等信息),它看起来好像线程池没有问题跟上生产者(侦听器线程)。
使用top -H检查CPU使用情况时,刚开始时分布相当均匀,但最后工作线程几乎从不活动,只有侦听器线程活动。但似乎并没有提交更多的任务...
有人能够假设这些症状的原因吗?您认为更可能是遗留代码(我无法控制)中存在问题,当添加多个线程时会出现问题吗?内存不足的问题应该是因为某个队列增长过大,但由于线程池几乎从不包含排队任务,所以不能是那个问题。
欢迎任何想法。特别是如何更有效地诊断此类情况的想法。我如何更好地了解我的线程正在做什么等等。
谢谢。

黑盒子是终端组件还是连接的?它是被动组件还是主动组件?你的线程池是从哪里获取的? - alphazero
请提供有关黑盒的更多细节。 - toto2
黑盒只是一个内部函数,它接受类型为A的对象,并对它们执行各种操作以生成类型为B的对象。这是应用程序的计算部分,也是池中线程执行的部分。线程池是标准的Oracle/Sun JDK实现。 - UmaN
4个回答

5

减速然后内存耗尽意味着存在内存泄漏。

因此,我建议使用一些Java内存分析工具来确定是否存在泄漏以及正在泄漏什么。有时你会很幸运地发现泄漏的对象是众所周知的,并且很清楚谁在坚持不放它们。


4
感谢您的回答。我了解了Java VisualVM并将其用作工具。结果和结论如下详细说明。希望图片能够持续有效。
我首先运行程序并创建了一些堆转储,以为我可以分析这些转储并查看占用所有内存的内容。这可能会奏效,但是由于转储文件过大,我的工作站无法使用。在等待一个操作两个小时后,我意识到我做不到这一点。
所以我的下一个选择是我愚蠢地没有想到的。我只需减少发送到应用程序的消息数量,内存使用趋势仍然存在。此外,转储文件将更小且分析速度更快。
事实证明,当以较慢的速率发送消息时,不会发生内存不足问题!下面是内存使用情况的图表。

slow send

这些峰值是累积内存分配的结果,随后的低谷是垃圾收集器运行后的结果。虽然内存使用量确实相当令人担忧,可能存在问题,但没有观察到长期的内存泄漏趋势。

我开始逐步增加每秒发送的消息速率,以查看应用程序的性能极限。下面的图片显示了一个非常不同的情况...

fast send

由于消息发送速率增加而导致此问题发生,我猜测释放监听器线程会导致它能够快速接受大量消息,从而导致越来越多的分配。垃圾收集器不运行,内存使用量达到极限。
当然,这个问题还有更多细节,但是根据今天发现的情况,我已经有了一个相当好的想法可以继续解决。当然,欢迎任何额外的建议/评论。
这个问题可能应该被重新分类为处理内存使用而不是线程池... 线程池根本不是问题所在。

2
我同意@djna的看法。Java并发包中的线程池很有效。只有在需要时才会创建线程,你可以看到线程数符合预期。这意味着你的旧代码可能没有准备好多线程。例如,某些代码片段未进行同步。结果是集合中的某个元素未被删除,或者其他额外的元素被存储在集合中,因此内存使用量增加。
顺便说一下,我不确定应用程序的哪个部分现在使用了线程池。你以前只有一个处理事件的线程,现在有几个线程来执行此操作吗?你可能改变了线程间通信机制,添加了队列吗?这也可能是你调查的另一个方向。
祝你好运!

1

正如djna所提到的,这很可能是某种类型的内存泄漏。我猜测你在某个地方保留了对请求的引用:

  • 在排队请求的调度线程中
  • 在处理请求的线程中
  • 在处理请求的黑盒子中
  • 在写入磁盘的写入线程中。

由于你说在将线程池加入混合之前一切都正常工作,我的猜测是线程池中的线程在某个地方保留了对请求的引用。这样做的想法是,如果没有线程池,你就不能重复使用线程,因此信息会消失。

如djna所建议的,你可以使用Java内存分析器来帮助确定数据堆积的位置。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接