17

Ich stelle diese Frage, weil ich viele Executor-Dienste erstelle und während ich vielleicht bereits irgendwo ein Speicherleck habe, das untersucht werden muss, denke ich, dass eine kürzliche Änderung am folgenden Code es tatsächlich verschlimmert hat, daher versuche ich es um zu bestätigen, was los ist:Ruft ein ExecutorService Garbage Collected ab, wenn der Scope außerhalb des Bereichs liegt?

@FunctionalInterface 
public interface BaseConsumer extends Consumer<Path> { 
    @Override 
    default void accept(final Path path) { 
     String name = path.getFileName().toString(); 
     ExecutorService service = Executors.newSingleThreadExecutor(runnable -> { 
      Thread thread = new Thread(runnable, "documentId=" + name); 
      thread.setDaemon(true); 
      return thread; 
     }); 
     Future<?> future = service.submit(() -> { 
      baseAccept(path); 
      return null; 
     }); 
     try { 
      future.get(); 
     } catch (InterruptedException ex) { 
      Thread.currentThread().interrupt(); 
     } catch (ExecutionException ex) { 
      throw new RuntimeException(ex); 
     } 
    } 

    void baseAccept(final Path path) throws Exception; 
} 

Dann wird diese Consumer<Path> auf einem anderen Thread-Pool mit (meist) genannt N = 2 Fäden, bin ich nicht sicher, ob das relevant ist.

Die Frage ist: Ist die ExecutorService service den Gültigkeitsbereich verlassen und bekommen Müll gesammelt einmal BaseConsumer#accept beendet hat?

+3

Ich verstehe das nicht. Warum gibt es einen Executor-Service, wenn Sie trotzdem auf 'future.get()' warten wollen? Der Dienst wird Müll gesammelt, wenn alle laufenden Threads darin aufhören. In Ihrem Fall ist die Ausführung immer synchron, da Sie 'submit()' und mit 'get()' warten. –

+0

@DeepakBala Ich möchte die Ausführung auf einem benannten Thread stattfinden lassen, es hat damit zu tun, dass es leichter zu protokollieren ist. In jedem Fall sollte dieser Code funktionieren. – skiwi

+3

Das ist kein guter Grund, den Overhead des Erstellens eines Executors zu durchlaufen. Ich bestreite nicht, dass dein Code funktioniert. Es wird. Sie sind besser dran, wenn Sie das synchron ausführen und das Protokoll optimieren, um zu schreiben, was Sie wollen. Ja, der Service wird Müll gesammelt werden. In Ihrem Fall besteht kein Zweifel daran, dass die Threads, die vor der Ausführung abgeschlossen werden, den Anwendungsbereich der Methode verlassen. –

Antwort

16

Hat der ExecutorService Dienst außerhalb des Gültigkeitsbereichs gehen und Müll einmal BaseConsumer.accept() beendet hat gesammelt werden?

Ja.

In der Tat sollte der zugehörige Thread-Pool auch Müll gesammelt werden ... schließlich.

Die ExecutorService, die von Executors.newSingleThreadExecutor() eine Instanz von FinalizableDelegatedExecutorService erstellt wird. Diese Klasse hat finalize() Methode, die shutdown() auf dem umschlossenen ExecutorService Objekt aufruft. Vorausgesetzt, dass alle ausstehenden Aufgaben tatsächlich beendet werden, wird das Dienstobjekt seinen Threadpool herunterfahren.

(.. AFAIK ist dies nicht angegeben, aber es ist das, was nach dem Quellcode implementiert ist, in Java 6 ab)


Hat eine finally {service.shutdown Zugabe(); } in der try-catch um future.get() Hilfe beim schnellen Abrufen von Ressourcen? (nicht unbedingt Müll sammeln das Service-Objekt).

Ja, tut es. Der Aufruf von shutdown() bewirkt, dass die Threads freigegeben werden, sobald die ausstehenden Aufgaben abgeschlossen sind. Dieser Vorgang wird sofort gestartet, während Sie ihn, wenn Sie ihn einfach dem Garbage Collector überließen, erst starten würden, wenn der Finalizer aufgerufen wurde.

Nun, wenn die Ressourcen nur "gewöhnliche" Java-Objekte wären, wäre das egal. Aber in diesem Fall ist die Ressource, die Sie zurückfordern, ein Java-Thread und hat Betriebssystemressourcen (z. B. einen nativen Thread) und einen nicht-trivialen Chunk von Nicht-Heap-Speicher. Es lohnt sich also, dies zu tun.

Wenn Sie jedoch nach einer Optimierung suchen, sollten Sie möglicherweise ein langlebiges ExecutorService-Objekt erstellen und es für mehrere "Consumer" -Instanzen freigeben.

+2

Fügt ein 'finally {service.shutdown(); } 'in der Try-Catch um' future.get() 'Hilfe beim * Ressourcen * schneller abrufen? (nicht unbedingt Müll sammeln das 'Service'-Objekt) – skiwi

7

Ich möchte die Ausführung auf einem benannten Thread stattfinden lassen, es hat damit zu tun, dass es leichter zu protokollieren ist. In jedem Fall sollte dieser Code funktionieren.

können Sie tun dies viel einfacher/schneller

Thread t = Thread.currentThread(); 
String name = t.getName(); 
try { 
    t.setName("My new thread name for this task"); 
    // do task 
} finally { 
    t.setName(name); 
} 

diese Weise können Sie einen Namen Thread, ohne eine neue verwenden können.

+0

Sie haben einen Punkt dort. +1 – skiwi

+0

@skiwi Es ist ein bisschen ein Hack, aber alles, was Sie wirklich wollen, ist ein vernünftiger Thread-Name für den Logger auszudrucken. BTW Ändert auch die 'Jstack'-Ausgabe. Das Schöne daran ist, dass Sie in einem Thread einen fortlaufenden Stack-Trace und nicht einen Teil des Stacks sehen. –

Verwandte Themen