2017-06-22 7 views
0

Ich verwende Formscanner und seine nach ein paar Bilder seiner Angabe der Fehler bei der Verarbeitung:Exception in thread „main“ java.lang.OutOfMemoryError: unable neue native Thread erstellen

Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread 
at java.lang.Thread.start0(Native Method) 
at java.lang.Thread.start(Thread.java:717) 
at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:950) 
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1357) 
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134) 
at com.albertoborsetta.formscanner.api.FormTemplate.findPoints(FormTemplate.java:852) 
at com.albertoborsetta.formscanner.model.FormScannerModel.analyzeFiles(FormScannerModel.java:562) 
at com.albertoborsetta.formscanner.main.FormScanner.main(FormScanner.java:145) 

der Fund Punkte-Methode ist als unter:

public void findPoints(BufferedImage image, int threshold, int density, 
     int size) throws FormScannerException { 
    height = image.getHeight(); 
    width = image.getWidth(); 
    int cores = Runtime.getRuntime().availableProcessors(); 

    ExecutorService threadPool = Executors.newFixedThreadPool(cores - 1); 
    HashSet<Future<HashMap<String, FormQuestion>>> fieldDetectorThreads = new HashSet<>(); 

    HashMap<String, FormQuestion> templateFields = template.getFields(); 
    ArrayList<String> fieldNames = new ArrayList<>(templateFields.keySet()); 
    Collections.sort(fieldNames); 

    for (String fieldName : fieldNames) { 
     Future<HashMap<String, FormQuestion>> future = threadPool.submit(new FieldDetector(threshold, density, size, this, templateFields.get(fieldName), image)); 
     fieldDetectorThreads.add(future); 
    } 

    for (Future<HashMap<String, FormQuestion>> thread : fieldDetectorThreads) { 
     try { 
      HashMap<String, FormQuestion> threadFields = thread.get(); 
      for (String fieldName : threadFields.keySet()) { 
       FormQuestion field = threadFields.get(fieldName); 
       fields.put(fieldName, field); 
       for (Entry<String, FormPoint> point : field.getPoints().entrySet()) { 
        if (point.getValue() != null) { 
         pointList.add(point.getValue()); 
        } 
       } 
      } 
     } catch (InterruptedException | ExecutionException e) { 
      throw new FormScannerException(e.getCause()); 
     } 
    } 

    threadPool.shutdown(); 

} 

die obige Funktion in der Schleife und die Anzahl von Java-Prozesse genannt wird wächst und an einem Punkt stellt sich die obige Ausnahme.

Gibt es eine Möglichkeit, dass diese Threads getötet wurden, nachdem die Shutdown-Methode aufgerufen wurde. Ich bin kein Java-Entwickler. Ich habe ein paar R & D. Aber ich bin nicht erfolgreich.

+0

Betreiben Sie eine 32 oder 64 Bit JVM? Was ist das Betriebssystem? Was sind die Speichereinstellungen (Xmx, Xms, Xss ...)? –

+0

Der Pool soll die Anzahl der laufenden Threads begrenzen und Sie haben ein 'thread.get', das darauf wartet, dass jeder Thread endet. Wenn Sie also diese Methoden nicht in neuen Threads aufrufen (die im Stacktrace sichtbar wären), sehe ich nicht, wie dies überladen könnte. ** Aber natürlich könnte es die erste laufende FieldDetectors-Instanz sein, die den gesamten verfügbaren Speicher belegt **. Was macht die FieldDetector-Instanz? – AxelH

+0

Ich benutze ubuntu 64bit mit openjdk – Baran

Antwort

2

Das Problem kommt von der Set<Future>, die verwendet wird, um jede Instanz zu halten, um sie später zu überprüfen.

Im Chat haben Sie mir gesagt, dass Sie 120.000 Dateien überprüft haben. Das bedeutet, dass es so viele Future gibt, die erstellt werden, wenn der Pool einen Slot findet, wird ein Thread erstellt, um den Callable auszuführen.

Da die Set jede Instanz halten, sind die Thread nicht Müll gesammelt, das, was Sie das Leck geben. Sie müssen alle verwendeten Future entfernen, damit der GC den Speicher für die nächste Thread löscht.

einen Iterator anstatt die Schleife selbst zu verwenden ist einfach und lassen Sie sich vor der Anwendung die aktuelle Instanz entfernen

Iterator<Future<HashMap<String, FormQuestion>>> iterator = fieldDetectorThreads.iterator(); 
while (iterator.hasNext()) { 
    //get the next instance 
    Future<HashMap<String, FormQuestion>> thread = iterator.next(); 
    //Remove it from the set 
    iterator.remove(); 

    //then work on that instance just like before 
    try { 
     HashMap<String, FormQuestion> threadFields = thread.get(); 
     for (String fieldName : threadFields.keySet()) { 
      FormQuestion field = threadFields.get(fieldName); 
      fields.put(fieldName, field); 
      for (Entry<String, FormPoint> point : field.getPoints().entrySet()) { 
       if (point.getValue() != null) { 
        pointList.add(point.getValue()); 
       } 
      } 
     } 
    } catch (InterruptedException | ExecutionException e) { 
     throw new FormScannerException(e.getCause()); 
    } 
} 

Diese Lösung ist nicht getestet, aber dies sollte den Speicher schnell genug freigeben können.

Aber wenn die Schleife zum Senden der Anfrage dauerte zu viel Zeit zu beenden (120k Zukunft zu generieren, bevor Sie die erste), würde dies brechen, bevor jede Anfrage gesendet werden.

In diesem Fall kann es notwendig sein, diese Logik in zwei Threads zu teilen, einen zum Senden einer Anfrage, einen zum Überprüfen des Ergebnisses, bis der erste Thread beendet ist und der Satz leer ist.


Nur für den Fall, würde ich nach der Schleife

threadPool.shutdown(); 

eine Shutdown-Anfrage hinzufügt es nicht erforderlich sein soll, aber seltsamerweise mein Testprogramm nicht zu Ende, ohne es ... auch wenn jeder Thread wurden bearbeitet, sie scheinen zu existieren und blockieren den Hauptthread.

+0

Bitte helfen Sie mir, die findCorners Methode der Threads zu beheben. – Baran

+0

Ich habe den Code zurückgesetzt, wenn ich 1000 Blatt verarbeitet habe, haben Sie den gleichen Effekt 513 Prozesse wurden ausgeführt, als ich nach Ihrem Vorschlag geändert. – Baran

Verwandte Themen