Führt das Erstellen eines Objekts mithilfe von Reflektion anstelle des Aufrufs des Klassenkonstruktors zu signifikanten Leistungsunterschieden?
java
performance
optimization
reflection
dmanxiii
quelle
quelle
Antworten:
Ja absolut. Das Nachschlagen einer Klasse durch Reflexion erfolgt nach Größenordnung teurer.
Zitieren von Javas Dokumentation zur Reflexion :
Hier ist ein einfacher Test, den ich in 5 Minuten auf meinem Computer mit Sun JRE 6u10 gehackt habe:
Mit diesen Ergebnissen:
Denken Sie daran, dass die Suche und die Instanziierung zusammen durchgeführt werden. In einigen Fällen kann die Suche auch überarbeitet werden. Dies ist jedoch nur ein einfaches Beispiel.
Selbst wenn Sie nur instanziieren, erhalten Sie dennoch einen Leistungseinbruch:
Wieder YMMV.
quelle
Ja, es ist langsamer.
Aber denken Sie an die verdammte Regel Nr. 1: OPEMATUROPTIMIERUNG IST DIE WURZEL ALLEN BÖSEN
(Nun, kann mit # 1 für DRY verbunden sein)
Ich schwöre, wenn jemand bei der Arbeit auf mich zukam und mich danach fragte, würde ich in den nächsten Monaten sehr wachsam über ihren Code sein.
Sie dürfen niemals optimieren, bis Sie sicher sind, dass Sie es brauchen. Bis dahin schreiben Sie einfach guten, lesbaren Code.
Oh, und ich meine auch nicht, dummen Code zu schreiben. Denken Sie nur an die sauberste Art und Weise, wie Sie dies tun können - kein Kopieren und Einfügen usw. (Seien Sie immer noch vorsichtig bei Dingen wie inneren Schleifen und verwenden Sie die Sammlung, die Ihren Anforderungen am besten entspricht. Das Ignorieren dieser Programme ist keine "nicht optimierte" Programmierung , es ist "schlechte" Programmierung)
Es macht mich verrückt, wenn ich Fragen wie diese höre, aber dann vergesse ich, dass jeder alle Regeln selbst lernen muss, bevor er sie wirklich versteht. Sie erhalten es, nachdem Sie einen Monat lang mit dem Debuggen von etwas "Optimiertem" verbracht haben.
BEARBEITEN:
In diesem Thread ist etwas Interessantes passiert. Überprüfen Sie die Antwort Nr. 1. Dies ist ein Beispiel dafür, wie leistungsfähig der Compiler bei der Optimierung von Dingen ist. Der Test ist vollständig ungültig, da die nicht reflektierende Instanziierung vollständig herausgerechnet werden kann.
Lektion? Optimieren Sie NIEMALS, bis Sie eine saubere, sauber codierte Lösung geschrieben und bewiesen haben, dass sie zu langsam ist.
quelle
Möglicherweise stellen Sie fest, dass A a = new A () von der JVM optimiert wird. Wenn Sie die Objekte in ein Array einfügen, sind sie nicht so leistungsfähig. ;) Die folgenden Drucke ...
Dies deutet darauf hin, dass der Unterschied auf meinem Computer etwa 150 ns beträgt.
quelle
Class.getDeclaredMethod
) und dannMethod.invoke
mehrmals anrufe ? Benutze ich die Reflexion einmal oder so oft, wie ich sie aufrufe? Folgefrage, was ist, wenn es stattdessenMethod
eine istConstructor
und ich esConstructor.newInstance
mehrmals mache ?Wenn wirklich etwas schneller als Reflexion benötigt wird und es nicht nur eine vorzeitige Optimierung ist, dann die Bytecode-Generierung mit ASM oder einer höheren Bibliothek eine Option. Das erstmalige Generieren des Bytecodes ist langsamer als nur das Reflektieren. Sobald der Bytecode generiert wurde, ist er genauso schnell wie normaler Java-Code und wird vom JIT-Compiler optimiert.
Einige Beispiele für Anwendungen, die die Codegenerierung verwenden:
Das Aufrufen von Methoden für von CGLIB generierte Proxys ist etwas schneller als die dynamischen Proxys von Java , da CGLIB Bytecode für seine Proxys generiert, dynamische Proxys jedoch nur Reflexion verwenden ( ich habe gemessen, dass CGLIB bei Methodenaufrufen etwa 10-mal schneller ist, aber das Erstellen der Proxys war langsamer).
JSerial generiert einen Bytecode zum Lesen / Schreiben der Felder serialisierter Objekte, anstatt Reflektion zu verwenden. Es gibt einige Benchmarks auf der Website von JSerial.
Ich bin nicht 100% sicher (und ich habe jetzt keine Lust, die Quelle zu lesen), aber ich denke, Guice generiert Bytecode, um die Abhängigkeitsinjektion durchzuführen. Korrigiere mich, wenn ich falsch liege.
quelle
"Signifikant" ist völlig kontextabhängig.
Wenn Sie Reflection verwenden, um ein einzelnes Handlerobjekt basierend auf einer Konfigurationsdatei zu erstellen, und dann den Rest Ihrer Zeit damit verbringen, Datenbankabfragen auszuführen, ist dies unbedeutend. Wenn Sie eine große Anzahl von Objekten durch Reflexion in einer engen Schleife erstellen, ist dies von Bedeutung.
Im Allgemeinen sollte die Designflexibilität (wo erforderlich!) Die Verwendung von Reflexion und nicht die Leistung fördern. Um jedoch festzustellen, ob die Leistung ein Problem darstellt, müssen Sie ein Profil erstellen, anstatt willkürliche Antworten von einem Diskussionsforum zu erhalten.
quelle
Es gibt einen gewissen Overhead bei der Reflexion, aber auf modernen VMs ist er viel kleiner als früher.
Wenn Sie Reflektion verwenden, um jedes einfache Objekt in Ihrem Programm zu erstellen, stimmt etwas nicht. Es gelegentlich zu verwenden, wenn Sie einen guten Grund haben, sollte überhaupt kein Problem sein.
quelle
Ja, es gibt einen Leistungseinbruch bei der Verwendung von Reflection, aber eine mögliche Problemumgehung für die Optimierung ist das Zwischenspeichern der Methode:
wird darin enden, dass:
[Java] Das 1000000-malige reflexive Aufrufen der Methode mit Lookup dauerte 5618 Millis
[Java] Das 1000000-malige reflexive Aufrufen der Methode mit dem Cache dauerte 270 Millis
quelle
Die Reflexion ist langsam, obwohl die Objektzuweisung nicht so hoffnungslos ist wie andere Aspekte der Reflexion. Um eine gleichwertige Leistung mit reflexionsbasierter Instanziierung zu erzielen, müssen Sie Ihren Code schreiben, damit der JIT erkennen kann, welche Klasse instanziiert wird. Wenn die Identität der Klasse nicht ermittelt werden kann, kann der Zuordnungscode nicht eingefügt werden. Schlimmer noch, die Escape-Analyse schlägt fehl und das Objekt kann nicht gestapelt werden. Wenn Sie Glück haben, kann die Laufzeitprofilerstellung der JVM Abhilfe schaffen, wenn dieser Code heiß wird, und dynamisch bestimmen, welche Klasse vorherrscht, und für diese optimiert werden.
Beachten Sie, dass die Mikrobenchmarks in diesem Thread stark fehlerhaft sind. Nehmen Sie sie daher mit einem Körnchen Salz. Der mit Abstand am wenigsten fehlerhafte ist der von Peter Lawrey: Er führt Aufwärmläufe durch, um die Methoden zu verbessern, und er besiegt (bewusst) die Fluchtanalyse, um sicherzustellen, dass die Zuweisungen tatsächlich erfolgen. Sogar das hat seine Probleme: Zum Beispiel kann erwartet werden, dass die enorme Anzahl von Array-Speichern Caches und Speicherpuffer besiegt, so dass dies meistens ein Speicher-Benchmark wird, wenn Ihre Zuweisungen sehr schnell sind. (Ein großes Lob an Peter, dass er zu dem Schluss gekommen ist, dass der Unterschied "150 ns" und nicht "2,5x" ist. Ich vermute, dass er so etwas für seinen Lebensunterhalt tut.)
quelle
Interessanterweise führt die Einstellung von setAccessible (true), bei der die Sicherheitsüberprüfungen übersprungen werden, zu einer Kostenreduzierung von 20%.
Ohne setAccessible (true)
Mit setAccessible (true)
quelle
1000000
Aufrufe ausgeführt werden?setAccessible()
kann es im Allgemeinen viel mehr Unterschiede geben, insbesondere bei Methoden mit mehreren Argumenten, daher sollte es immer aufgerufen werden.Ja, es ist deutlich langsamer. Wir haben einen Code ausgeführt, der dies tat, und obwohl mir die Metriken derzeit nicht zur Verfügung stehen, war das Endergebnis, dass wir diesen Code umgestalten mussten, um keine Reflektion zu verwenden. Wenn Sie wissen, was die Klasse ist, rufen Sie einfach den Konstruktor direkt auf.
quelle
In doReflection () ist der Overhead aufgrund von Class.forName ("misc.A") (für das eine Klassensuche erforderlich wäre, die möglicherweise den Klassenpfad auf dem Dateisystem scannt) und nicht die für die Klasse aufgerufene newInstance (). Ich frage mich, wie die Statistiken aussehen würden, wenn Class.forName ("misc.A") nur einmal außerhalb der for-Schleife ausgeführt wird. Dies muss nicht unbedingt bei jedem Aufruf der Schleife erfolgen.
quelle
Ja, es wird immer langsamer sein, ein Objekt durch Reflektion zu erstellen, da die JVM den Code zur Kompilierungszeit nicht optimieren kann. Weitere Informationen finden Sie in den Sun / Java Reflection-Tutorials .
Siehe diesen einfachen Test:
quelle
Class.forName()
) von der Instanziierung (newInstance ()) trennen sollten , da sie sich in ihren Leistungsmerkmalen erheblich unterscheiden und Sie die wiederholte Suche in einem gut gestalteten System gelegentlich vermeiden können.Oft können Sie Apache Commons BeanUtils oder PropertyUtils verwenden, die eine Introspektion durchführen (im Grunde werden die Metadaten über die Klassen zwischengespeichert, sodass sie nicht immer Reflektion verwenden müssen).
quelle
Ich denke, es hängt davon ab, wie leicht / schwer die Zielmethode ist. Wenn die Zielmethode sehr leicht ist (z. B. Getter / Setter), kann sie 1 bis 3 Mal langsamer sein. Wenn die Zielmethode etwa 1 Millisekunde oder mehr dauert, ist die Leistung sehr nahe. Hier ist der Test, den ich mit Java 8 und Reflectasm durchgeführt habe :
Der vollständige Testcode ist unter GitHub verfügbar: ReflectionTest.java
quelle