2009-12-01 17 views
9

Ich habe das Verständnis, dass Perm-Größe verwendet wird, um Metadaten zu speichern, die Byte-Code, statische Sachen usw. enthalten würde.Wie wirkt sich Reflektion auf die Perm-Größe aus?

Meine Frage wie wirkt sich die Nutzung der Reflexion auf die Dauerwelle, wenn es jemals tut. Ich meine, wenn Programm-A nutzt normale Art und Weise der laufenden Objekte und Programm-B Reflexion überall verwendet, wie werden die Perm-Größen der beiden Programme vergleichen?

Antwort

7

Der zul Raum wird wachsen, wenn Sie Code ausführen, wird laden Neue Klassen oder Strings internalisieren. Die Reflexionsklassen müssen geladen werden, das ist sicher. Ich bin mir nicht sicher, ob die Reflektions-API stark internalisierte Strings verwendet, aber das sollte nicht schwer zu finden sein.

Zum Beispiel für die Methode getDeclaredMethod(String name, Class<?>... parameterTypes) wird der Name Parameter internalisiert.

Dieser Ausschnitt wird schön den Perm Raum füllt:

Random random = new Random(); 
while(true){ 
    try { 
     X.class.getDeclaredMethod(toBinaryString(random.nextLong())); 
    } catch (Exception e) { 
    } 
} 

alt text http://img4.imageshack.us/img4/9725/permgen.jpg

In einer realen Welt Anwendung wird es keinen Unterschied machen.

+0

Was ich glaube, dass Sie dort sehen, ist das Internieren des gesuchten Namens, so dass '==' für die bereits intern gespeicherten Methodennamen verwendet werden kann. (Nicht überzeugt, dass das eine großartige Idee ist.) Im Gesicht kann Reflexion manchmal (manchmal) Klassen erzeugen. –

+0

(Diese intern gespeicherten Strings sind GCable mit einem vollständigen Speicherbereinigungszyklus, eine gute Implementierungsqualität vorausgesetzt.) –

+0

@Tom, können Sie ein einzelnes Beispiel angeben, in dem eine Klasse aufgrund von Reflektion erstellt wird? Ich versuche nicht, ein Schmerz zu sein, ich würde wirklich gerne von diesen Fällen wissen, da ich noch nie einen gesehen habe. – PSpeed

3

Ich bin mir nicht ganz sicher, ob ich die Frage verstehe, aber Java-Klassen enthalten bereits alles, was nötig ist, um über sie nachzudenken. Eine .class-Datei ändert sich nicht basierend auf ihrer Verwendung.

Edit 1: Bereitstellung einiger konkreter Beispiele zur besseren Unterscheidung der Art von "Reflexion", über die wir sprechen.

Beispiel:

class Foo { 
    public void bar() { 
     System.out.println("Hello, world."); 
    } 
} 

// Conventional calling 
Foo f = new Foo(); 
foo.bar(); 

// Reflection based callling 
Class fooClass = Class.forName("Foo"); 
Method m = fooClass.getMethod("bar", null); 
Object f = fooClass.newInstance(); 
m.invoke(m, null);  

Im herkömmlichen calling Fall Foo.class und eines seiner direkten Klasse Abhängigkeiten geladen. Bar wird ausgeführt. Die Zeichenfolgen "Foo" und "bar" wurden bereits als Teil der aufrufenden Klasse interniert, da der Byte-Code Strings verwendet, um die Klassen und Methoden zur Laufzeit aufzulösen. (eigentlich "bar" wird die volle Methodensignatur also eigentlich länger als nur "bar" sein)

Im Reflektionsfall passiert genau das Gleiche. Die einzige zusätzliche geladene Klasse ist Method.class. Das sollte der einzige Einfluss auf die Größe der Dauerwelle sein.

Der letztere Fall hat Auswirkungen auf die Leistung. Das Methoden-Lookup ist relativ teuer, daher ist es ratsam, Methodenobjekte im Cache zu speichern. Der Aufruf der zusätzlichen Methode zum Aufrufen hat eine geringe Auswirkung auf die Leistung, da es sich um einen zusätzlichen Methodenaufruf handelt. Hotspot wird Probleme haben, durch diesen Anruf zu optimieren ... zumindest mehr als normal. JITing passiert genau gleich.

Edit 2: einige zusätzliche Objekte Anbetracht dessen, dass bei der Reflexion ...

java.lang.Class erstellen und Cache-Method-Objekte (oder Feldobjekte, etc.) beim Zugriff geladen werden. Diese werden in einer SoftReference zwischengespeichert und daher zurückgewonnen, wenn die Speichernutzung dies erfordert.

Die Initialisierung dieser Objekte bedeutet jedoch, dass zusätzliche interne Strings von der VM geladen werden können, um die Erstellung dieser Method-Objekte zu unterstützen. Meine Vermutung ist, dass diese Strings wahrscheinlich bereits Teil des konstanten Pools der reflektiven Klasse waren, aber es ist möglich, dass dies nicht der Fall ist. So oder so, es ist ein einmaliger Treffer pro Methode pro Klasse, pro Feld pro Klasse, etc .. Greifen Sie auf die Methoden zu, Sie erhalten mindestens alle Namen für diese Methoden interniert. Greifen Sie auf die Felder zu, Sie erhalten die Namen dieser Felder interniert.

+0

Klassen können dynamisch durch die Reflektionsimplementierung für schnellen Zugriff generiert werden. –

+0

Referenz bitte? Ich benutze die ganze Zeit Reflektion (wirklich ... die ganze Zeit) und habe noch nie eine einzige Klasse als Resultat gesehen. Third-Party-Bibliotheken können einige Dinge tun und das dynamische Proxy-Zeug wird definitiv eine Laufzeitklasse generieren. Das einfache Aufrufen von Methoden für eine Klasse/ein Objekt sollte dies nicht tun. – PSpeed

+0

@ PSpeed..pls überprüfen Sie den folgenden Link https://jaxb.dev.java.net/issues/show_bug.cgi?id=581 –

2

Die erste Antwort ist grundsätzlich richtig - ob eine Klasse "normal" oder über Reflexion geladen wird, spielt keine Rolle. So oder so ist es geladen. Ich kann mir keinen Unterschied in den internierten Strings vorstellen, die ich kenne.

Ich nehme an, dass es einen indirekteren Effekt gibt: Mit Reflection können Sie weniger Klassen laden. Nehmen wir an, Sie haben einen Code, der basierend auf einem Parameter eine von 100 Klassen lädt. Wenn Sie es ohne Nachdenken schreiben, würden Sie alle 100 Klassen importieren und eines davon instanziieren.

Mit Reflektion würde nur 1 Klasse geladen werden. Ohne das Laden der Klasse mit diesem Code würden alle 100 importierten Klassen geladen, da alle referenziert werden. Also, mehr Klassen werden in den Dauerraum geladen.

Aber das ist ein etwas erfundenes Beispiel. Es sei denn, es hat deine Situation wirklich beschrieben, ich nehme an, du wirst praktisch keinen Unterschied bemerken. Das heißt, bis Sie sicher sind, dass es nicht trivial ist, lassen Sie das nicht Ihre Designentscheidungen beeinflussen.

3

Reflection kann je nach Implementierung Klassen generieren. Sie müssen zum Beispiel ein Reflexionsartefakt tausende Male verwenden, bevor es zu Bytecode kompiliert wird.

Wie immer lautet der Rat: Profilieren Sie Ihren Code in realistischen Situationen.

+0

Meinst du "nativen Code" oben? Die Klassen sind bereits Bytecode. Hotspot wird Probleme haben, durch Aufrufe der Reflexion zu optimieren, aber das JIT verhält sich genauso. – PSpeed

+0

Nein, ich meine Bytecode. Offensichtlich kompiliert HotSpot nach der Verwendung den Bytecode zu nativ. Felder können gelesen werden, indem der Offset vom Anfang des Objekts gefunden und hinzugefügt wird. Sie können auch gelesen werden, indem ein Byte-Code geschrieben wird, der diese Felder liest (wobei Zugriffsrechte ignoriert werden). So etwas geht weiter (in der Sun JRE), aber ich vergesse die Details. –

+0

+1: Aktivieren Sie einfach GC-Protokoll und überprüfen Sie System.out - [Entladen Klasse sun.reflect.GeneratedMethodAccessor1403] [Entladen Klasse sun.reflect.GeneratedConstructorAccessor446] [Entladen Klasse sun.reflect.GeneratedMethodAccessor562] - Diese werden durch die Reflexion generiert Implementierung. – fglez

Verwandte Themen