使用执行器的java内存泄漏。newFixedThreadPool()
我正在使用Spring3。1在独立环境上
(这个问题与Spring无关。它在独立环境中的行为也一样。)
我实现了一个侦听器,它接收来自主题的消息。消息速率非常高(约为20/30 m/s)
某些消息可能需要比其他消息更多的处理时间
监听器处理同一个实例,这意味着如果一条消息被处理的时间太长,它会严重影响我们的性能
我们考虑使用自己的对象池,而不是使用同一个侦听器实例,但后来我发现了执行器(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);
}
这似乎解决了我们的性能问题。但是在使用jconsole监控应用程序之后,我们现在面临着巨大的内存泄漏
堆内存使用在时间上显著增加
因此,我尝试使用FixedThreadPool大小的数字“玩”一点。仍有大量内存使用:
你知道我该怎么解决这个问题吗?还有其他解决我关键问题的办法吗
运行堆转储后,我发现了两个可疑问题:
谢谢, 雷
# 1 楼答案
我相信您遇到的问题是threadPool没有释放其资源。你需要打电话给threadPool。完成提交或执行后关闭()。这将等到任务完成后再终止线程,然后可以对线程进行垃圾收集
从Java api官方网站:
“应关闭未使用的ExecutorService,以便回收其资源。” https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html#shutdown()
或者,您可以使用newCachedThreadPool(),它“创建一个线程池,根据需要创建新线程,但在可用时将重用以前构造的线程”,请参见https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html
遇到这个问题时,我使用了newFixedThreadPool()和shutdown选项
# 2 楼答案
只是一个建议,但如果你每秒创建30条消息,而计算机(即使并行)处理这30条消息的时间超过1秒,那么提交任务的队列将无法控制地增长。如果队列大小大于设定值,则应确保不提交任何任务,并等待一段时间。每个消息对象都使用内存,我认为这可能是你的问题。cachedthreadpool无法解决此问题
你可以通过打印你的队列大小来测试这一点。不知道这个问题是否已经解决了
# 3 楼答案
在使用ExecutorService之后,您需要停止线程池,因为它是同一资源,比如文件、数据库或任何其他需要显式释放的资源。ExecutorService有方法shutdown()和shutdownNow(),可以在finally块中使用它们来gargabe collect
如果忘记关闭,就会发生内存泄漏,这也类似于JVM通常不关闭的流,因为默认执行器不会创建守护进程线程
# 4 楼答案
我想你没有内存泄漏的问题,至少从你的jconsole图表上看不出来。没有主要的GC收集。因此,似乎只有越来越多的对象被分配给终身(老)一代。为了确保内存泄漏,您应该执行GC,然后比较分配的内存。如果发现泄漏,可以使用jmap或可视化工具(standart JDK tools)进行堆转储。在此之后,可以使用MAT分析堆转储。在进行堆转储之前,最好执行GC以减小堆转储文件的大小
注意: