2017-06-22 2 views
0

J'utilise Formscanner et son traitement après quelques images de son donnant l'erreur:Exception dans le thread « principal » java.lang.OutOfMemoryError: impossible de créer un nouveau fil natif

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) 

la méthode des points FIND est aussi dans:

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(); 

} 

la fonction ci-dessus est appelée dans la boucle et le nombre de processus Java se développe et en un point soulève l'exception ci-dessus.

Est-il possible que ces threads soient arrêtés après l'appel de la méthode shutdown? Je ne suis pas un développeur Java. J'ai fait quelques R & D. Mais je ne réussis pas.

+0

Courez-vous une JVM de 32 ou 64 bits? Quel est le système d'exploitation utilisé? Quels sont les paramètres de la mémoire (Xmx, Xms, Xss ...)? –

+0

Le pool est supposé limiter le nombre de threads en cours d'exécution et vous avez un 'thread.get' qui attend que chaque thread se termine. Donc, à moins que vous n'appeliez cette méthode dans de nouveaux threads (qui seraient visibles dans la stacktrace), je ne vois pas comment cela pourrait être surchargé. ** Mais bien sûr, il pourrait s'agir de la première instance de FieldDetectors qui prend toute la mémoire disponible **. Que va faire l'instance de FieldDetector? – AxelH

+0

Im utilisant Ubuntu 64bit avec openjdk – Baran

Répondre

2

Le problème vient du Set<Future> utilisé pour contenir chaque instance pour les vérifier plus tard.

En discussion, vous m'avez dit que vous étiez en train de vérifier 120.000 fichiers. Cela signifie qu'il y a beaucoup de Futures créés, quand le pool trouve un slot, il créera un Thread pour exécuter le Callable.

Depuis le Set tenir chaque instance, les Thread ne sont pas ramassés, que ce qui vous donne la fuite. Vous devez supprimer chaque Future utilisé pour laisser le GC effacer la mémoire pour le prochain Thread.

En utilisant un itérateur au lieu de la boucle elle-même est simple et vous permettre de supprimer l'instance en cours avant l'utilisation

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

Cette solution n'a pas été testé, mais cela devrait être en mesure de libérer la mémoire assez vite. Mais si la boucle pour soumettre la requête prenait beaucoup de temps à finir (120k future à générer avant de vérifier la première), cela se casserait avant que toutes les requêtes ne soient envoyées.

Dans ce cas, il peut être nécessaire de diviser cette logique en deux threads, l'un pour envoyer la requête, l'autre pour vérifier le résultat jusqu'à ce que le premier thread soit terminé et que l'ensemble soit vide.


Juste au cas où, je voudrais ajouter après la boucle une demande d'arrêt

threadPool.shutdown(); 

il ne devrait pas être nécessaire, mais étrangement mon programme de test ne se termine pas sans elle ... même si tous les thread ont été traités, ils semblent continuer à exister et bloquer le thread principal.

+0

S'il vous plaît aidez-moi à corriger la méthode de threads findCorners. – Baran

+0

J'ai retourné le code quand j'ai traité 1000 feuilles, il a le même effet 513 processus étaient en cours d'exécution que j'ai changé selon votre suggestion. – Baran