OutOfMemoryError: невозможно создать новый собственный поток - PullRequest
2 голосов
/ 01 ноября 2011

У меня есть два разных веб-приложения A и B, размещенные на tomcat.Приложение B размещено на 4 физических системах с отдельным 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)

Как решить вышеуказанную проблему?

Эти приложения работают на HTTPS на Tomcat 5.5.17 с jdk1.5.0_13

1 Ответ

4 голосов
/ 01 ноября 2011

Как решить вышеуказанную проблему?

Непосредственной причиной проблемы является нехватка памяти для создания новых потоков.(Эта память не выходит из обычной кучи, поэтому изменение -Xmx не поможет.)

Фактическая причина в том, что потоки создаются быстрее, чем они выходят.Я подозреваю , что это утверждение является корнем проблемы:

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

Проблема в том, что this будет экземпляром SendNotification, который вы использовали для отправки уведомления.Если вы повторно используете этот объект, вы обнаружите, что у вас есть серьезное узкое место, из-за которого сетевые задачи выполняются по одной за раз.

В дополнение к вышеуказанной проблеме ваш подход имеет недостатки, поскольку каждый раз, когда вывызов sendNotification, он перезаписывает состояние экземпляра SendNotification без какой-либо синхронизации.Проще говоря, вам не следует использовать одноэлементное состояние для хранения состояния для нескольких потоков ...


Другая возможность состоит в том, что вы не используете повторно экземпляры SendNotification и (следовательно) синхронизациюпроблемы не являются причиной проблемы.В этом случае следующая возможная причина заключается в том, что вы просто создаете новые потоки быстрее, чем сервер может их обработать.

Непосредственное решение состоит в том, чтобы использовать ограниченный пул потоков, чтобы у вас никогда не было более чем, скажем, 20 потоков активных одновременно.Существуют стандартные классы для такого рода действий.

Однако будет ограничение на скорость, с которой ваше приложение / платформа может выполнять эти задачи.Если вы превысите этот лимит в течение длительного периода, очереди вашего приложения будут накапливаться, и вы столкнетесь с проблемами.Если это вероятно, тогда вашему приложению нужна какая-то стратегия для снижения нагрузки.


Хммм ...

Похоже, что стек SSL создает поток для выполнения согласования SSLна заднем фоне.Похоже, вы открываете соединение с удаленным сервером, а затем немедленно закрываете его, ничего не читая.Возможно, этот необычный шаблон использования приводит к утечке потока согласования SSL.Попробуйте прочитать байт из потока, прежде чем закрыть его.Это может привести к тому, что текущий поток будет join поток согласования SSL, и он не останется в подвешенном состоянии.

Если это то, что происходит, то это, вероятно, ошибка в коде Java SSLSocketImplты используешь.Вам следует обновить JVM и Tomcat, чтобы получить самые последние (и не самые последние!) Исправления безопасности, производительности и исправления ошибок.Возможно, это не решит эту проблему ... но вы все равно должны это сделать.

ОБНОВЛЕНИЕ - Поиск в Google для "утечки ошибок SSLSocketImpl" дает много попаданий.Я не нашел тот, который точно соответствует, но, тем не менее, рекомендуется обновить.

...