Ein Kollege sagte mir, dass die Erstellung von Java-Objekten die teuerste Operation ist, die Sie ausführen können. Daraus kann ich nur den Schluss ziehen, so wenig Objekte wie möglich zu erstellen.
Dies scheint den Zweck der objektorientierten Programmierung etwas zu entkräften. Wenn wir keine Objekte erstellen, schreiben wir nur einen langen C-Stil zur Optimierung.
Antworten:
Ihr Kollege hat keine Ahnung, wovon er spricht.
Ihre teuerste Operation wäre es, ihnen zuzuhören . Sie haben Ihre Zeit damit verschwendet, Sie zu Informationen zu verleiten, die mehr als ein Jahrzehnt veraltet sind (ab dem ursprünglichen Datum, an dem diese Antwort veröffentlicht wurde), und Sie mussten Zeit damit verbringen, hier zu posten und im Internet nach der Wahrheit zu suchen.
Hoffentlich erbrechen sie etwas, das sie vor mehr als einem Jahrzehnt gehört oder gelesen haben, nur unwissentlich und wissen es nicht besser. Ich würde alles andere, was sie sagen, auch als verdächtig ansehen. Dies sollte ein bekannter Irrtum von jedem sein, der so oder so auf dem Laufenden bleibt.
Alles ist ein Objekt (außer
primitives
)Alles andere als Grundelemente (
int, long, double
usw.) sind Objekte in Java. Es gibt keine Möglichkeit, die Objekterstellung in Java zu umgehen.Die Objekterstellung in Java ist aufgrund seiner Speicherzuweisungsstrategien in den meisten Fällen schneller als in C ++ und kann für alle praktischen Zwecke im Vergleich zu allem anderen in der JVM als "frei" betrachtet werden .
Bereits in den frühen 2000er-Jahren der späten 1990er-Jahre hatten JVM-Implementierungen einen gewissen Performance-Overhead bei der tatsächlichen Zuweisung von Objekten. Dies ist seit mindestens 2005 nicht mehr der Fall.
Wenn Sie so einstellen
-Xms
, dass der gesamte für die ordnungsgemäße Ausführung Ihrer Anwendung erforderliche Arbeitsspeicher unterstützt wird, muss der GC in den modernen GC-Implementierungen möglicherweise nie ausgeführt und den größten Teil des Mülls beseitigt werden.Es wird nicht versucht, den freien Speicherplatz zu maximieren, was ohnehin ein roter Hering ist, sondern es wird die Leistung der Laufzeit maximiert. Wenn dies bedeutet, dass der JVM-Heap fast immer zu 100% zugeordnet ist, ist dies auch der Fall. Freier JVM-Heapspeicher gibt Ihnen sowieso nichts, was Sie nur dort sitzen.
Es besteht ein Missverständnis, dass der GC auf nützliche Weise Speicher für den Rest des Systems freigibt. Dies ist völlig falsch!
Der JVM-Heap wächst und schrumpft nicht, sodass der Rest des Systems durch den freien Speicher im JVM-Heap positiv beeinflusst wird .
-Xms
Weist ALLES zu, was beim Start angegeben wird, und seine Heuristik besteht darin, nie wirklich etwas von diesem Speicher für das Betriebssystem freizugeben, um es mit anderen Betriebssystemprozessen zu teilen, bis diese Instanz der JVM vollständig beendet wird.-Xms=1GB -Xmx=1GB
Weist 1 GB RAM zu, unabhängig davon, wie viele Objekte tatsächlich zu einem bestimmten Zeitpunkt erstellt wurden. Es gibt einige Einstellungen, mit denen Prozentsätze des Heapspeichers freigegeben werden können. In der Praxis kann die JVM jedoch nie genug Speicher freigeben, damit dies jemals passieren kannDaher kann kein anderer Prozess diesen Speicher zurückfordern, sodass auch der Rest des Systems nicht davon profitiert, dass der JVM-Heap-Speicher frei ist. Eine RFE dafür wurde am 29. November 2006 "akzeptiert" , aber es wurde noch nie etwas dagegen unternommen. Dieses Verhalten wird von niemandem mit Autorität als bedenklich angesehen.Es gibt ein Missverständnis, dass das Erstellen vieler kleiner, kurzlebiger Objekte dazu führt, dass die JVM für längere Zeit pausiert. Dies ist nun ebenfalls falsch
Derzeitige GC-Algorithmen sind für die Erstellung vieler kleiner Objekte mit kurzer Lebensdauer optimiert. Dies ist im Grunde die 99% -ige Heuristik für Java-Objekte in jedem Programm. Versuche, Objekte zusammenzufassen , führen in den meisten Fällen zu einer Verschlechterung der Leistung der JVM.
Die einzigen Objekte, die heute gepoolt werden müssen, sind Objekte, die sich auf endliche Ressourcen außerhalb der JVM beziehen . Sockets, Dateien, Datenbankverbindungen usw. können wiederverwendet werden. Regelmäßige Objekte können nicht werden gepoolt im gleichen Sinne wie in Sprachen , die Sie direkten Zugriff auf Speicherplätze ermöglichen. Objekt- Caching ist ein anderes Konzept und kann oder kann nicht das sein, was manche Leute naiv als Pooling bezeichnen . Die beiden Konzepte sind nicht dasselbe und sollten nicht miteinander verschmolzen werden.
Die modernen GC-Algorithmen haben dieses Problem nicht, da sie nicht nach einem Zeitplan freigegeben werden, sondern freigegeben werden, wenn in einer bestimmten Generation freier Speicher benötigt wird. Wenn der Heap groß genug ist, werden die Zuordnungen nicht lange genug aufgehoben, um Pausen zu verursachen.
Objektorientiert Dynamische Sprachen schlagen C auch heute noch bei rechenempfindlichen Tests.
quelle
_alloca
amortisiert arbeitet.new
Schlüsselwort an einem Hotspot verwenden. Ich habe Leute verwenden gesehennew ImageIcon(Image)
in derpaint()
Methode der Swing - Objekte, die ziemlich teuer ist und war die gesamte UI Super träge macht. Es ist also keine Schwarz-Weiß-Antwort. Überlegen Sie, bevor Sie sienew
irgendwo verwenden.Fazit: Gehen Sie keine Kompromisse bei Ihrem Design ein, um Verknüpfungen zum Erstellen von Objekten zu erstellen. Vermeiden Sie es, unnötig Objekte zu erstellen. Wenn sinnvoll, sollten Sie redundante Vorgänge (jeglicher Art) vermeiden.
Im Gegensatz zu den meisten Antworten - ja, die Objektzuweisung ist mit Kosten verbunden. Es ist kostengünstig, aber Sie sollten vermeiden, unnötige Objekte zu erstellen. Das Gleiche, wie Sie unnötige Elemente in Ihrem Code vermeiden sollten. Große Objektdiagramme verlangsamen die GC, was längere Ausführungszeiten mit sich bringt, da Sie wahrscheinlich mehr Methodenaufrufe haben, mehr CPU-Cache-Ausfälle auslösen und die Wahrscheinlichkeit erhöhen, dass Ihr Prozess in Fällen mit geringem Arbeitsspeicher auf die Festplatte ausgelagert wird.
Bevor sich jemand darüber beschwert, dass dies ein Randfall ist, habe ich Anwendungen profiliert, die vor der Optimierung 20 + MB Objekte erstellt haben, um ~ 50 Datenzeilen zu verarbeiten. Dies ist beim Testen in Ordnung, bis Sie auf 100 Anforderungen pro Minute skalieren und plötzlich 2 GB Daten pro Minute erstellen. Wenn Sie 20 Reqs / Sek. Ausführen möchten, erstellen Sie 400 MB Objekte und werfen sie dann weg. 20 reqs / sec ist für einen anständigen Server winzig.
quelle
while(something) { byte[] buffer = new byte[10240]; ... readIntoBuffer(buffer); ...
das zu verwenden verschwenderisch im Vergleich zu zbyte[] buffer = new byte[10240]; while(something) { ... readIntoBuffer(buffer); ...
.Aufgrund der Speicherverwaltungsstrategien, die die Java-Sprache (oder eine andere verwaltete Sprache) ermöglicht, ist die Objekterstellung kaum mehr als das Inkrementieren eines Zeigers in einem Speicherblock, der als junge Generation bezeichnet wird. Es ist viel schneller als C, wo nach freiem Speicher gesucht werden muss.
Der andere Teil der Kosten betrifft die Zerstörung von Objekten, ist aber schwer mit C zu vergleichen. Die Kosten einer Sammlung basieren auf der Menge der Objekte, die langfristig gespeichert wurden, aber die Häufigkeit der Sammlungen basiert auf der Menge der Objekte, die erstellt wurden Am Ende ist es immer noch viel schneller als die Speicherverwaltung im C-Stil.
quelle
Point
Objekt in zwei Universalregister passen).Andere Poster haben zu Recht darauf hingewiesen, dass die Objekterstellung in Java extrem schnell ist und dass Sie sich in einer normalen Java-Anwendung normalerweise keine Gedanken darüber machen sollten.
Es gibt ein paar ganz besondere Situationen , in denen sind ist eine gute Idee , Objekterstellung zu vermeiden.
quelle
In dem, was Ihr Kollege sagt, steckt ein Kern der Wahrheit. Ich schlage vor , respektvoll das Problem mit dem Objekt Schöpfung ist eigentlich Müll Sammlung . In C ++ kann der Programmierer genau steuern, wie der Speicher freigegeben wird. Das Programm kann so lange oder so kurz wie es möchte Rohöl akkumulieren. Darüber hinaus kann das C ++ - Programm den Crud mit einem anderen Thread als dem, der ihn erstellt hat, verwerfen. Somit muss der aktuell funktionierende Thread nie aufhören, um aufzuräumen.
Im Gegensatz dazu stoppt die Java Virtual Machine (JVM) Ihren Code regelmäßig, um nicht verwendeten Speicherplatz freizugeben. Die meisten Java-Entwickler bemerken diese Pause nie, da sie normalerweise selten und sehr kurz ist. Je mehr Rohstoffe Sie ansammeln oder je eingeschränkter Ihre JVM ist, desto häufiger treten diese Pausen auf. Sie können Tools wie VisualVM verwenden , um diesen Prozess zu visualisieren.
In neueren Versionen von Java kann der Garbage Collection (GC) -Algorithmus optimiert werden . Je kürzer die Pausen sein sollen, desto teurer ist in der Regel der Overhead in der virtuellen Maschine (dh die CPU und der Speicher müssen für die Koordinierung des GC-Prozesses aufgewendet werden).
Wann könnte das von Bedeutung sein? Immer wenn Sie sich für gleichbleibende Sub-Millisekunden-Antwortraten interessieren, interessiert Sie GC. In Java geschriebene automatisierte Handelssysteme optimieren die JVM stark, um Pausen zu minimieren. Unternehmen, die sonst Java schreiben würden, greifen auf C ++ zurück, wenn Systeme ständig sehr reaktionsschnell sein müssen.
Ich kann das Vermeiden von Objekten im Allgemeinen nicht gutheißen! Standardmäßig wird die objektorientierte Programmierung verwendet. Passen Sie diesen Ansatz nur an, wenn der GC Ihnen im Weg ist, und erst dann, wenn Sie versuchen, die JVM so zu optimieren, dass sie eine kürzere Pause einlegt. Ein gutes Buch über Java Performance Tuning ist Java Performance von Charlie Hunt und Binu John.
quelle
Es gibt einen Fall, in dem Sie möglicherweise aufgrund des Overheads davon abgehalten werden, zu viele Objekte in Java zu erstellen - das Entwerfen für die Leistung auf der Android-Plattform
Ansonsten gelten die obigen Antworten.
quelle
Der GC ist auf viele kurzlebige Objekte abgestimmt
das heißt, wenn Sie die Objektzuordnung trivial reduzieren können, sollten Sie
Ein Beispiel wäre das Bauen eines Strings in einer Schleife, der naive Weg wäre es
Hierdurch
String
wird für jede+=
Operation ein neues Objekt erstellt (plus einStringBuilder
und das neue zugrunde liegende Zeichenarray).Sie können dies leicht umschreiben in:
Dieses Muster (unveränderliches Ergebnis und ein lokal veränderlicher Zwischenwert) kann auch auf andere Dinge angewendet werden
Ansonsten sollten Sie einen Profiler aufrufen, um den echten Engpass zu finden, anstatt Geister zu jagen
quelle
StringBuilder
Ansatz ist die vorge Größe derStringBuilder
so dass es nie zu hat umverteilen den zugrunde liegenden Array mit demStringBuilder(int)
Konstruktor. Dies macht es zu einer einzelnen Zuordnung und nicht zu einer1+N
Zuordnung.Joshua Bloch (einer der Entwickler der Java-Plattform) schrieb 2001 in seinem Buch Effective Java :
quelle
Dies hängt wirklich von der spezifischen Anwendung ab, daher ist es im Allgemeinen sehr schwer zu sagen. Ich wäre jedoch ziemlich überrascht, wenn die Objekterstellung tatsächlich ein Leistungsengpass in einer Anwendung wäre. Selbst wenn sie langsam sind, überwiegt der Vorteil des Code-Stils wahrscheinlich die Leistung (es sei denn, der Benutzer merkt es tatsächlich.)
In jedem Fall sollten Sie sich nicht einmal Gedanken über diese Dinge machen, bis Sie Ihren Code profiliert haben, um die tatsächlichen Leistungsengpässe zu ermitteln, anstatt zu raten. Bis dahin sollten Sie alles tun, was für die Lesbarkeit des Codes am besten ist, nicht die Leistung.
quelle
Ich denke, Ihr Kollege muss aus der Perspektive der unnötigen Objekterstellung gesagt haben. Ich meine, wenn Sie häufig dasselbe Objekt erstellen, ist es besser, dieses Objekt gemeinsam zu nutzen. Selbst in Fällen, in denen die Objekterstellung komplex ist und mehr Speicher benötigt, möchten Sie das Objekt möglicherweise klonen und vermeiden, dass dieser komplexe Objekterstellungsprozess erstellt wird (dies hängt jedoch von Ihren Anforderungen ab). Ich denke, die Aussage "Objekterstellung ist teuer" sollte im Kontext gesehen werden.
Warten Sie in Bezug auf den JVM-Speicherbedarf auf Java 8. Sie müssen nicht einmal -Xmx angeben. Die Meta-Space-Einstellungen berücksichtigen den JVM-Speicherbedarf und werden automatisch erweitert.
quelle
Das Erstellen einer Klasse umfasst mehr als das Zuweisen von Speicher. Es gibt auch Initialisierung, ich bin mir nicht sicher, warum dieser Teil nicht von allen Antworten abgedeckt wird. Normale Klassen enthalten einige Variablen und führen eine Art Initialisierung durch, die nicht kostenlos ist. Je nach Klasse werden möglicherweise Dateien gelesen oder andere langsame Vorgänge ausgeführt.
Überlegen Sie sich also, was der Klassenkonstruktor macht, bevor Sie herausfinden, ob er kostenlos ist oder nicht.
quelle
Javas GC ist tatsächlich sehr optimiert, um eine Vielzahl von Objekten in einem "Burst" -Verfahren schnell zu erstellen. Soweit ich verstehen kann, verwenden sie einen sequentiellen Allokator (den schnellsten und einfachsten O (1) Allokator für Anfragen variabler Größe) für diese Art von "Burst-Zyklus" in einem Speicherbereich, den sie "Eden-Raum" nennen, und nur dann, wenn die Objekte bestehen bleiben Nach einem GC-Zyklus werden sie an einen Ort gebracht, an dem der GC sie nacheinander einsammeln kann.
Wenn Ihre Leistungsanforderungen jedoch kritisch genug werden (gemessen an den tatsächlichen Anforderungen der Benutzer), sind Objekte mit einem Mehraufwand verbunden, aber ich würde bei der Erstellung / Zuweisung nicht so viel darüber nachdenken. Dies hat mehr mit der Referenzlokalität und der zusätzlichen Größe aller Objekte in Java zu tun, die für die Unterstützung von Konzepten wie Reflektion und dynamischem Versand erforderlich sind (
Float
ist größer alsfloat
, bei 64-Bit mit seinen Ausrichtungsanforderungen häufig etwa viermal so groß wie) Array vonFloat
wird nicht unbedingt zusammenhängend von dem gespeichert, was ich verstehe).Eines der auffälligsten Dinge, die ich je in Java gesehen habe und die mich zu einem Schwergewichts-Konkurrenten auf meinem Gebiet (VFX) gemacht haben, war ein interaktiver Multithread-Standard-Pfad-Tracer (ohne Caching der Bestrahlungsstärke oder BDPT oder MLS oder irgendetwas anderes) Die CPU liefert Echtzeitvorschauen, die relativ schnell zu einem rauschfreien Bild zusammenlaufen. Ich habe mit Profis in C ++ zusammengearbeitet, die sich mit ausgefallenen Profilern, die Schwierigkeiten damit hatten, diesen Dingen verschrieben haben.
Aber ich habe mich im Quellcode umgesehen und zwar unter Verwendung einer Vielzahl von Objekten zu geringen Kosten, aber die kritischsten Teile des Pfadverfolgers (BVH und Dreiecke und Materialien) haben Objekte sehr klar und absichtlich zugunsten großer Arrays primitiver Typen vermieden ( meistens
float[]
undint[]
), was dazu führte, dass viel weniger Speicher verbraucht wurde und die räumliche Lokalität garantiert wurde, um von einemfloat
Array zum nächsten zu gelangen. Ich denke nicht, dass es zu spekulativ ist, das zu denken, wenn der Autor Boxed Types verwendet, die ihm gefallenFloat
Dort hätte es einen ziemlich hohen Preis für seine Leistung. Aber wir sprechen über den absolut kritischen Teil dieser Engine, und ich bin mir ziemlich sicher, dass der Entwickler diese Optimierung gemessen und sehr, sehr vernünftig angewendet hat, da er Objekte überall gerne verwendet hat Trivialkosten für seinen beeindruckenden Echtzeit-Pfadfinder.Selbst in einem so leistungskritischen Bereich wie meinem werden Sie keine effizienten Produkte schreiben, wenn Sie sich an Orten einklemmen, die keine Rolle spielen. Ich würde sogar die Behauptung aufstellen, dass die leistungskritischsten Bereiche möglicherweise eine noch höhere Nachfrage nach Produktivität aufwerfen, da wir die gesamte zusätzliche Zeit benötigen, um die Hotspots zu optimieren, die wirklich wichtig sind, indem wir keine Zeit mit Dingen verschwenden, die dies nicht tun . Wie im obigen Beispiel für den Pfadfinder hat der Autor solche Optimierungen geschickt und vernünftig nur auf die Stellen angewendet, die wirklich, wirklich von Bedeutung waren, und wahrscheinlich nach dem Vermessen im Nachhinein und immer noch überall gern verwendete Objekte.
quelle
float[]
gegenüberFloat[]
, wo die Verarbeitung eine Million der früheren sequentiell könnte relativ ziemlich schneller als die zweiten sein.Wie die Leute bereits sagten, ist das Erstellen von Objekten in Java kein großer Aufwand (aber ich wette, es ist größer als die meisten einfachen Operationen wie Hinzufügen usw.), und Sie sollten es nicht zu sehr vermeiden.
Trotzdem sind die Kosten weiterhin hoch, und manchmal werden Sie versuchen, so viele Objekte wie möglich zu verschrotten. Aber erst nachdem die Profilerstellung gezeigt hat, dass dies ein Problem ist.
Hier ist eine großartige Präsentation zum Thema: https://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdf
quelle
Ich habe diesbezüglich ein kurzes Mikrobenchmark durchgeführt und vollständige Quellen in Github bereitgestellt. Mein Fazit ist, dass es nicht darum geht, ob das Erstellen von Objekten teuer ist oder nicht, sondern dass das kontinuierliche Erstellen von Objekten mit dem Gedanken, dass der GC die Dinge erledigt, dafür sorgt, dass Ihre Anwendung den GC-Prozess früher auslöst. GC ist ein sehr teurer Prozess, und es ist am besten, ihn zu vermeiden, wann immer es möglich ist, und nicht zu versuchen, ihn einzuschalten.
quelle