我在一个独立的环境中使用Spring3.1。
(这个问题不一定与Spring有关。它在独立的环境中也表现相同。)
我实现了一个从Topic接收消息的监听器。消息率非常高(约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);
}
这似乎解决了我们的性能问题。但在使用jconsole监控应用程序后,我们现在面临巨大的内存泄漏。
堆内存使用量在时间上显着增加。
所以我尝试使用FixedThreadPool大小编号“玩”一下。仍有大量内存使用:
我知道如何解决这个问题?还有其他想法来解决我的关键问题吗?
运行堆转储后,我遇到了两个问题:
感谢, 射线。
答案 0 :(得分:2)
我认为你对内存泄漏没有任何问题,我至少无法从你的jconsole图表中看到它。没有主要的GC收集。因此,似乎只有越来越多的对象被分配到终身(旧)代。为了确保内存泄漏,您应该执行GC,然后比较分配的内存。如果发现泄漏,您可以使用jmap或可视化工具(标准JDK工具)进行堆转储。可以使用MAT分析此堆转储。在进行堆转储之前,最好执行GC以减少堆转储文件大小。
一些注意事项:
答案 1 :(得分:1)
我相信你遇到的问题是threadPool没有释放它的资源。完成提交或执行后,需要调用threadPool.shutdown()。这将等到任务完成后再终止线程,然后可以进行垃圾回收。
来自官方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 :(得分:0)
在使用ExecutorService之后,您需要停止线程池,因为它是相同的资源,如文件或数据库或需要显式发布的任何其他内容。 ExecutorService有方法shutdown()和shutdownNow(),可以在finally块中使用gargabe collect。
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class SimpExec {
public static void main(String args[]) {
CountDownLatch countDownLatch = new CountDownLatch(5);
CountDownLatch CountDownLatch2 = new CountDownLatch(5);
ExecutorService eService = Executors.newFixedThreadPool(2);
eService.execute(new MyThread(countDownLatch, "A"));
eService.execute(new MyThread(CountDownLatch2, "B"));
try {
countDownLatch.await();
CountDownLatch2.await();
} catch (InterruptedException exc) {
System.out.println(exc);
}
finally{
eService.shutdown(); // This is the method we use to avoid memory Leaks.
// eService.shutdownNow(); // -do-
}
}
}
class MyThread implements Runnable {
String name;
CountDownLatch latch;
MyThread(CountDownLatch c, String n) {
latch = c;
name = n;
new Thread(this);
}
public void run() {
for (int i = 0; i < 5; i++) {
latch.countDown();
}
}
}
如果您忘记关闭,则会发生内存泄漏,它也像JVM通常不会关闭的流,因为默认的Executors不会创建守护程序线程。
答案 3 :(得分:0)
只是一个建议,但如果你每秒创建30条消息并且计算机(甚至并行)需要更长时间来处理这30条消息而不是1秒,那么你提交的任务队列将无法控制地增长。如果队列大小大于设定的数量并等待一下,则应确保没有提交任务。每个Message对象都使用内存,我认为这可能是你的问题。 cachedthreadpool无法解决此问题。
您可以通过打印出队列大小来完成测试。不知道这是否已经解决了......