Ist das Poolen von Objekten eine veraltete Technik?

62

Ich bin mit dem Konzept des Objekt-Poolings sehr vertraut und versuche es immer so oft wie möglich anzuwenden.

Außerdem dachte ich immer, dass Objekt-Pooling die Standardnorm ist, da ich beobachtet habe, dass Java selbst sowie die anderen Frameworks so viel wie möglich Pooling verwenden.

Kürzlich habe ich jedoch etwas gelesen, das für mich völlig neu (und nicht intuitiv?) War.

Dieses Pooling führt zu einer Verschlechterung der Programmleistung, insbesondere bei gleichzeitigen Anwendungen, und es ist ratsam, newstattdessen Objekte zu instanziieren , da die Instanziierung eines Objekts in neueren JVMs sehr schnell ist.

Ich habe das im Buch gelesen: Java Concurrency in Practice

Jetzt beginne ich zu überlegen, ob ich hier etwas missverstehe, da im ersten Teil des Buches empfohlen wurde, Executorsdiese Wiederverwendung zu verwenden , Threadanstatt neue Instanzen zu erstellen.

Ist das Pooling von Objekten heutzutage veraltet?

user10326
quelle

Antworten:

72

Es wird als allgemeine Technik abgelehnt, da - wie Sie bemerkt haben - die Erstellung und Zerstörung von kurzlebigen Objekten per se (dh Speicherzuweisung und GC) in modernen JVMs äußerst kostengünstig ist. Die Verwendung eines handgeschriebenen Objektpools für Ihre Standardobjekte ist daher wahrscheinlich langsamer, komplizierter und fehleranfälliger als normal new. *

Es hat jedoch immer noch seine Verwendung für spezielle Objekte, deren Erstellung relativ kostspielig ist, wie DB- / Netzwerkverbindungen, Threads usw.

* Einmal musste ich die Leistung einer durchsuchenden Java-App verbessern. Die Untersuchung ergab, dass versucht wurde, mithilfe eines Objektpools Millionen von Objekten zuzuweisen ... und der clevere Typ, der ihn geschrieben hat, verwendete eine einzige globale Sperre, um ihn threadsicher zu machen. Durch das Ersetzen des Pools durch plain newwurde die App 30-mal schneller.

Péter Török
quelle
1
Wie kann man also entscheiden, ob die Instanziierung eines Objekts zu teuer ist?
user10326
3
Wenn das Objekt Betriebssystemressourcen (Threads, E / A, gemeinsamer Speicher usw.)
verbraucht
13
@ user10326, durch Messung :-) Wenn das Erstellen Ihrer Objekte viel Zeit in Anspruch nimmt und / oder sie mit einer speziellen, möglicherweise begrenzten Nicht-Speicherressource verknüpft sind, sollten Sie in Betracht ziehen, sie zusammenzufassen.
Péter Török
8
@ user10326, IMO In über 95% der Fälle können Sie anhand der oben genannten Kriterien im Voraus leicht entscheiden, ob Sie einen Objektpool benötigen. (Außerdem wird in fast allen Fällen, in denen ein Pool benötigt wird, höchstwahrscheinlich eine vorhandene Bibliothek / ein Framework verwendet, in dem der Objektpool wahrscheinlich bereits implementiert ist.) Im Übrigen ist es immer noch einfach, die Objekterstellung in z Eine Fabrik, die später nach Belieben neu implementiert werden kann.
Péter Török
2
Sehr wichtiger Punkt von @Peter Torok: Viele Frameworks und Bibliotheken implementieren das Pooling für Sie. Stellen Sie IMMER sicher, dass Sie noch keine gepoolte Bibliothek verwenden, bevor Sie Ihre eigene implementieren.
hromanko
36

Die Antwort auf die konkrete Frage: "Ist Objekt-Pooling eine veraltete Technik?" ist:

Objekt-Pooling wird häufig an bestimmten Stellen verwendet - Thread-Pooling, Datenbankverbindungs-Pooling usw.

Die allgemeine Objekterstellung war noch nie ein langsamer Prozess. Das Pooling an sich verbraucht Ressourcen - Speicher und Rechenleistung. Jede Optimierung ist ein Kompromiss.

Die Regel ist:

Vorzeitige Optimierung ist böse !!!

Aber wann ist eine bestimmte Optimierung verfrüht?

Vorzeitige Optimierung ist jede Optimierung, die durchgeführt wird, bevor Sie einen Engpass durch gründliche Profilerstellung aufgedeckt haben .

Boris Yankov
quelle
2
Tatsächlich. OP sagte "Ich versuche immer, es so oft wie möglich zu benutzen" - das ist das Problem, IMO.
Nerdytenor
@Boris, Also sollten wir gemäß Ihrem zweiten Satz keinen Einspruch gegen DB-Verbindungen und -Threads erheben, bis wir sie als Engpass durch Profilerstellung aufgedeckt haben?
Pacerier
1
@Pac Einige Profilerstellungsergebnisse müssen nicht ständig neu gemessen werden :-)
David Bullock
9

In Situationen, in denen Sie die Garbage Collection vollständig vermeiden möchten, halte ich das Objekt-Pooling für die einzig gangbare Alternative. Also nein, es ist absolut keine veraltete Technik.

Jer
quelle
1
Und ich würde hinzufügen, dass es eine gute Idee ist, GC zu vermeiden, wenn die Objekte langlebig genug sind, dass sie in die ältere Generation übergegangen sind.
Zan Lynx
8

Messen

Es hängt ganz von Ihrem Anwendungsfall, der Größe Ihrer Objekte, Ihrer JVM, Ihren JVM-Optionen, dem von Ihnen aktivierten GC und einer Reihe anderer Faktoren ab.

Kurz gesagt: Messen Sie es vorher und messen Sie es nachher. Angenommen, Sie verwenden ein Object Pooling Framework (wie von Apache), dann sollte es nicht zu schmerzhaft sein, zwischen Implementierungen zu wechseln.

Zusätzlicher Leistungstest-Tipp: Lassen Sie die JVM zuerst etwas aufwärmen und führen Sie die Tests mehrmals auf einer laufenden JVM aus. Sie kann sich auch anders verhalten.

Martijn Verburg
quelle
3
"Lassen Sie die JVM zuerst ein wenig aufwärmen" - ich erinnere mich, als das einzige, was "aufgewärmt" werden musste, der Monitor war. Oy, alles neue ist wieder alt.
kylben
Das Einzige, was ich zum Aufwärmen brauche, ist der Kaffee!
Desillusioniert
@Marijn, wie lässt du es "warm werden"?
Pacerier
Eine vollständige Erklärung finden Sie im JMH-Framework ( openjdk.java.net/projects/code-tools/jmh ). Grundsätzlich müssen Sie der JVM die Möglichkeit geben, Ihren Code zu JIT, GCs vor Ihrem Benchmark und so weiter.
Martijn Verburg
8

Dieses Pooling führt zu einer Verschlechterung der Programmleistung, insbesondere bei gleichzeitigen Anwendungen, und es ist ratsam, stattdessen neue Objekte zu instanziieren, da die Instanziierung eines Objekts in neueren JVMs sehr schnell ist.

Kommt auf den Kontext an.


quelle
1
Ausgezeichnete Antwort und überzeugend. Ich möchte hinzufügen (vielleicht als Sternchen?), Dass sich die Behauptung "24 Byte" auf 4 Instanzen von 4-Byte-Gleitkommazahlen (16 Byte) plus 4 Byte für die Objektreferenz plus 4 Byte für eine Sperrreferenz bezieht. Dies ist der exakte Aufwand, den Ihr Design eliminiert.
Strickli
5

Ich weiß nicht, ob es hier einen sich ändernden Trend gibt, aber es wird sicherlich der Fall sein, dass es davon abhängt . Wenn Ihre Java-Klasse eine externe Ressource verwaltet, z. B. eine RMI-Verbindung oder das Laden einer Ressourcendatei usw., können die Kosten für die Objektinstanziierung dennoch hoch sein (obwohl diese Ressourcen möglicherweise bereits für Sie zusammengefasst wurden!). Als allgemeine Praxis würde ich dem Buch zustimmen.

Jeremy
quelle
Nun, ich weiß es nicht. Da Sie auch in diesem Fall beschreiben, welches (bevor Sie dies lesen) ich definitiv als Pool verwenden würde, hätte ich auch Overhead. 1) Neue Konstrukte, um das Pooling zu handhaben aus dem Pool 3) Wartung des Pools usw. Also denke ich jetzt, dass es vielleicht keinen Anwendungsfall gibt, der nützlich ist, außer z. B. ein Socket zwischenzuspeichern, anstatt jedes Mal ein neues zu öffnen, um eine Verbindung zum Server herzustellen. Und dieser Fall liegt am Netzwerk
Wartezeit
@ user10326 Ja genau. Ich sehe das Öffnen eines Sockets als Teil des Instantiierungs-Overheads. Wenn es die Aufgabe der Klasse ist, dies zu tun, und es im Konstruktor initialisiert werden muss, sind die Latenz- und E / A-Auswirkungen das, worum es Ihnen geht.
Jeremy