OutOfMemoryError:无法创建新的本机线程

时间:2011-11-01 04:40:50

标签: java url tomcat tomcat5.5

我在tomcat上托管了两个不同的A和B Web应用程序。 B应用程序托管在具有单独tomcat的4个物理系统上。应用程序A托管在具有单独tomcat的第5个物理系统上。每当应用程序A上发生登录/注销事件时,它都需要将此通知发送到所有4个B应用程序。应用程序A为每个事件创建一个线程,该线程使用以下代码向所有4个B应用程序发出请求:

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;
  }

}

但是一段时间后tomcat开始抛出以下异常:

Exception in thread "qe01lj_login_success1320054407929" java.lang.OutOfMemoryError: unable to create new native thread
                at java.lang.Thread.start0(Native Method)
                at java.lang.Thread.start(Thread.java:574)
                at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:836)
                at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1030)
                at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1057)
                at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1041)
                at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:402)
                at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:170)
                at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:917)
                at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:234)
                at java.net.URL.openStream(URL.java:1007)
                at com.test. sendNotification.run(sendNotification.java:69)
                at java.lang.Thread.run(Thread.java:595)

如何解决上述问题?

这些应用程序在Tomcat 5.5.17上使用jdk1.5.0_13

运行HTTPS

1 个答案:

答案 0 :(得分:4)

  

如何解决上述问题?

问题的直接原因是您的内存不足以创建新线程。 (此内存不会出现在正常堆中,因此更改-Xmx将无济于事。)

实际原因是线程的创建速度比它们退出的速度快。我怀疑这个陈述是问题的根源:

synchronized (this) {
    // talk to network ...
}

问题是this将是您用来发送通知的SendNotification实例。如果重复使用该对象,您会发现存在严重的瓶颈,导致一次执行一个网络任务。

除了上述问题之外,您的方法存在缺陷,因为每次调用sendNotification时,它都会覆盖SendNotification实例的状态而不进行任何同步。简单地说,你不应该使用单例来保持多个线程的状态......


另一种可能性是您没有重用SendNotification实例,因此同步问题不是问题的原因。在这种情况下,下一个可能的原因是您只是创建新线程而不是服务器可以处理它们。

直接的解决方案是使用有界线程池,这样你一次就不会有超过(比方说)20个线程处于活动状态。有这样的标准类。

但是,您的应用程序/平台可以执行这些任务的速率会受到限制。如果您长时间超过该限制,您的应用程序的队列将会累积,您将遇到麻烦。如果可能的话,那么您的应用程序需要某种策略来减轻负载。


嗯...

SSL堆栈似乎正在创建一个在后台执行SSL协商的线程。您似乎正在打开与远程服务器的连接,然后立即关闭它...而不读取任何内容。也许这种不寻常的使用模式会导致SSL协商线程泄漏。在关闭之前尝试从流中读取一个字节。这可能会导致当前线程到join SSL协商线程,并且它不会处于不稳定状态。

如果这是正在发生的事情,那么它可能是您正在使用的Java SSLSocketImpl代码中的错误。您应该升级JVM和Tomcat以获取最新(并且不是最新的!)安全性,性能和错误修复。它可能无法解决这个问题......但无论如何你都应该这样做。

更新 - 谷歌搜索“SSLSocketImpl bug leak”给出了很多点击。我没有找到一个完全匹配的,但不过可以进行升级。