使用 Executors.newFixedThreadPool() 的内存泄漏

ray*_*man 6 java performance multithreading jms jakarta-ee

我在独立环境中使用 Spring3.1。

(此问题与 Spring 无关。它在独立环境中的行为也相同。)

我已经实现了一个从主题接收消息的监听器。消息速率非常高(大约为 20/30 m/s)。

有些消息可能比其他消息需要更多的处理时间。

侦听器使用相同的实例,这意味着如果一条消息处理时间过长,它会严重影响我们的表现。

我们考虑过使用我们自己的对象池而不是使用相同的侦听器实例,但后来我发现了Executors (java.util.concurrent.Executors)。

因此,对于收到的每条消息,都会为其分配不同的线程。这将确保我们的侦听器实例可以自由地并行处理消息。

private ExecutorService  threadPool = Executors.newFixedThreadPool(100);
    @Override
    public void onMessage(final Message msg)
    {
        Runnable t = new Runnable()
        {
            public void run()
            {
                onSessionMessage(msg);
                log.trace("AbstractSessionBean, received messge");
            }
        };
        threadPool.execute(t);
    }
Run Code Online (Sandbox Code Playgroud)

这似乎解决了我们的性能问题。但是在使用 jconsole 监视应用程序之后,我们现在面临着巨大的内存泄漏。

堆内存使用量随时间显着增加。

所以我试着用 FixedThreadPool 大小数字“玩”一下。仍然有巨大的内存使用量:

在此处输入图片说明

知道我该如何解决这个问题吗?还有其他想法可以解决我的关键问题吗?

执行 GB 后的 jconsole

jconsole 整体视图

运行 heap dump 后,我有两个问题嫌疑人:

头部转储

谢谢,雷。

Tak*_*aky 2

我认为你没有内存泄漏的问题,至少我无法从你的 jconsole 图表中看到它。没有主要的GC收集。因此,似乎只有越来越多的对象分配给终身(老)一代。为了确保内存泄漏,您应该执行 GC,然后比较分配的内存。如果发现泄漏,您可以使用 jmap 或可视化工具(标准 JDK 工具)进行堆转储。之后可以使用MAT分析该堆转储。在进行堆转储之前,最好执行 GC 以减少堆转储文件的大小。

一些注意事项:

  • 线程计数不应显式影响堆内存。回顾下一个java内存结构也许对你有用。所以线程需要堆栈内存而不是堆。
  • 一般来说,由于 GC 工作算法的原因,为不重的对象创建缓存不是一个好主意。
  • 另外,我认为您应该考虑缓存线程池或根据服务器硬件校准 ThreadPoolSize。