我遇到了明显的句柄泄漏,但是我希望有一种解决方法,或者我只是误解了JRE如何在内部处理线程。
请注意,此问题的答案不是“使用线程池并限制并发线程的最大数量”。这不能解决潜在的问题或可能的解决方案,特别是在需要大量最大同时线程的情况下。
如果我一次创建并启动许多线程,则会分配很多句柄。这是很合理的。但是,当这些线程全部结束时,大多数分配的句柄不会释放。
Process Explorer的句柄计数如下:
峰值-创建并运行600个线程后的最大句柄数
线程已结束-在所有600个线程都结束(仅离开主线程和JVM线程)之后处理计数
Handle Counts
JRE Peak Threads Ended
1.4 2632 232
5 3859 259
6 4311 3111
8 4321 3121
这里有两个主要观察结果:
重复创建和启动相同或更少数量的线程并不会增加Peak / Ended句柄计数(除非使用JRE 1.5,否则泄漏速度可能会更慢)。增加最大同时线程数会同时增加两个数字。
所有用于测试的JRE都是Sun / Oracle的。
GC从来没有出现过,并且根据几天的测试和观察问题来清理混乱。
有人知道如何确保在Java 6和更高版本中使用线程之后释放线程的句柄资源,而无需重新启动JVM吗?
重现该问题的测试代码如下:
public class ThreadLeakTest
{
public static void main(String[] args)
{
new ThreadLeakTest();
}
public ThreadLeakTest()
{
while (true)
{
for (int i = 0; i < 600; ++i)
{
LeakTestThread testThread = new LeakTestThread();
Thread t = new Thread(testThread);
t.start();
}
sleepSafe(20000);
}
}
private class LeakTestThread implements Runnable
{
public void run()
{
sleepSafe(15000);
}
}
private static final void sleepSafe(int sleepTime)
{
try
{
Thread.sleep(sleepTime);
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
}
答案 0 :(得分:0)
似乎是一个已知的错误,8154063:
VM维护一个PlatformEvent对象池(映射到Windows上的Events)。此池将一次达到VM中存在的并发线程的最大数量所需的最大值。我们倾向于先用完内存以在不使用Handles之前创建线程。因此,在第一次睡眠期间,仅存在主线程和几个VM线程,并且总句柄数量很少。
然后,我们启动3000个线程,每个线程至少使用5个Handle(并且根据报告的输出,我假设某处实现中有第6个隐藏项)。然后,根据计算机的大小和调度情况,并发线程数会有所不同,但是假设所有线程数均为3000,那么很容易看出峰值的来源。
因此,对于Windows,除了减少线程数量之外,似乎没有很好的解决方案。除此之外,还有一个更笼统的想法:似乎您将硬件用于“实际”服务器任务。如果是这样,请考虑: