Java OutOfMemoryError:无法创建新的本机线程
我在tomcat上托管了两个不同的A和B web应用程序。B应用程序托管在4个物理系统上,使用单独的tomcat。应用程序A通过单独的tomcat托管在第五个物理系统上。每当应用程序A上发生登录/注销事件时,它都需要将此通知发送给所有4个B应用程序。应用程序A为每个事件创建一个线程,该线程使用以下代码向所有4个B应用程序发出请求:Java OutOfMemoryError:无法创建新的本机线程,java,url,tomcat,tomcat5.5,Java,Url,Tomcat,Tomcat5.5,我在tomcat上托管了两个不同的A和B web应用程序。B应用程序托管在4个物理系统上,使用单独的tomcat。应用程序A通过单独的tomcat托管在第五个物理系统上。每当应用程序A上发生登录/注销事件时,它都需要将此通知发送给所有4个B应用程序。应用程序A为每个事件创建一个线程,该线程使用以下代码向所有4个B应用程序发出请求: public class SendNotification implements Runnable { private String action; priv
public class SendNotification implements Runnable {
private String action;
private String loginid;
private Thread thread;
StringBuilder sb = new StringBuilder();
public static SendNotification getInstance() {
return new SendNotification();
}
public void sendNotification(String action, String loginid){
this.action = action;
this.loginid = loginid;
sb.append(loginid)
.append("_")
.append(action)
.append(new Date().getTime());
thread = new Thread(this, sb.toString());
thread.start();
}
public void run(){
String methodName = "run";
int len = 4;
for(int i=0;i<len;i++){
synchronized(this){
StringBuilder url = new StringBuilder()
.append(<Server_B_i_IP:PORT>)
.append("&action=").append(action)
.append("&loginid=").append(loginid);
URL urlObj = null;
InputStream openStream = null;
try {
urlObj = new URL(url.toString());
openStream = urlObj.openStream();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
if(openStream != null){
openStream.close();
}
openStream = null;
urlObj = null;
url = null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
destroyObject();
}
private void destroyObject() {
thread = null;
}
如何解决上述问题
这些应用程序在带有jdk1.5.0_13的Tomcat 5.5.17上运行HTTPS
如何解决上述问题
问题的直接原因是内存不足,无法创建新线程。(此内存不是来自正常堆,因此更改-Xmx
不会有帮助。)
实际原因是创建线程的速度比退出线程的速度快。我怀疑这句话是问题的根源:
synchronized (this) {
// talk to network ...
}
问题在于此
将是您用来发送通知的发送通知
的实例。如果重用该对象,您会发现存在严重的瓶颈,导致网络任务一次只能执行一个
除上述问题外,您的方法还有缺陷,因为每次调用sendNotification
,它都会覆盖sendNotification
实例的状态,而不进行任何同步。简单地说,您不应该使用单例来保存多个线程的状态
另一种可能是您没有重用
SendNotification
实例,因此同步问题不是问题的原因。在这种情况下,下一个可能的原因是创建新线程的速度比服务器处理线程的速度快
直接的解决方案是使用一个有边界的线程池,这样一次活动的线程就不会超过(比如)20个。做这类事情有标准的类
但是,应用程序/平台执行这些任务的速度将受到限制。如果您在一段较长的时间内超过该限制,您的应用程序的队列将增加,您将遇到麻烦。如果这是可能的,那么您的应用程序需要某种减载策略
嗯 SSL堆栈似乎正在创建一个线程,以便在后台执行SSL协商。您似乎正在打开与远程服务器的连接,然后立即将其关闭。。。没有读任何东西。这种不寻常的使用模式可能会导致SSL协商线程泄漏。在关闭流之前,尝试从流中读取一个字节。这很可能会导致当前线程
加入到SSL协商线程中,而不会处于不确定状态
如果发生了这种情况,那么可能是您正在使用的Java SSLSocketImpl代码中的错误。您应该升级JVM和Tomcat,以获得最新的(不是最新的!)安全性、性能和bug修复。它可能无法解决此问题。。。但无论如何你都应该这么做
更新-用谷歌搜索“SSLSocketImpl漏洞泄漏”会带来很多点击率。我没有发现完全匹配的,但是升级是明智的。感谢Stephen的及时回复。如果查看getInstance()方法,它将创建SendNotification类的新对象。因此,它不是一个单例类。如果我删除同步块怎么办?这能解决问题吗?SendNotification类的每个实例都会创建一个新线程,该线程使用URL类的openStream()方法向所有4b应用程序发出请求。所以我的问题是,openStream()方法是否创建了与应用程序B通信的新线程?如果我们查看异常StackTrace,它显示无法在java.lang.thread.start(thread.java:574)的java.lang.thread.start0(本机方法)处创建新的本机线程,那么每个请求都有5个线程被创建?(一个是SendNotification类,四个是openStream()方法)?如果是,那么如果应用程序A同时获得100个登录请求,那么它将一次创建500个线程。以下是tomcat连接器端口配置:当应用程序A当时崩溃时,任务管理器为tomcat5.exe进程显示了1029个线程。在这里使用绑定队列的ThreadPoolExecutor比较合适。通过这个小小的改变,您将从线程管理中抽象出您的业务逻辑。第一眼看到它的原因似乎是线程的创建比它的销毁要快。
synchronized (this) {
// talk to network ...
}