2015-12-01 6 views
5

Welche Optimierungen würde Java Runtime im folgenden Codefragment durchführen? Der Bytecode zeigt keine Optimierung an, aber ich glaube, dass Java den letzten Wert der for-Schleife annehmen sollte, ohne die gesamte for-Schleife auszuführen, da String eine rudimentäre Java-Klasse ist.Java für Schleifenoptimierungen

HINWEIS. diese Frage wurde in einem Klassenversuch gestellt; Ich konnte jedoch nicht genügend Beweise vorlegen, um meine Behauptung zu stützen.

public class Main { 

    public static void main(String[] args) { 
     String str = null; 
     for (long i = 0; i < 10000000000L; i++) { 
      str = new String("T"); 
     } 

     System.out.println(str); 
    } 
} 
+4

Im Wesentlichen wird keine Optimierung auf Bytecode-Ebene durchgeführt; es ist fast alles zur Laufzeit. Das JIT ist _mostly_ eine magische Blackbox, über die man keine Garantien geben kann, aber es ist nicht außerhalb des Bereichs der Möglichkeiten, dass die ganze Schleife eliminiert werden könnte. –

+0

Sie können einen Blick darauf werfen, wie das JIT Ihren Code mit den folgenden Optionen zur JVM "verarbeitet": '-XX: + PrintCompilation -XX: + UnlockDiagnosticVMOptions -XX: + PrintInlining'. – fge

+0

@fge Die Entschlüsselung dieser Ausgabe ist eine ziemliche Herausforderung. Ich bin mir nicht ganz sicher, was dort vor sich geht. – bluejamesbond

Antwort

1

Während ich nicht genau sagen kann, was der JIT-Compiler tut, die Optimierung Sie es (um festzustellen, dass es sicher ist, zu überspringen, die Schleifenkörper vollständig) ist eigentlich sehr schwer zu tun, fragen zu tun und deshalb bezweifle ich sehr, dass es getan ist. Dies gilt unabhängig davon, ob String eine "rudimentäre Java-Klasse" ist.

Um das besser zu verstehen, nehmen wir als erstes an, dass wir anstelle von String Instanzen einer beliebigen Klasse Foo erzeugen. Es wäre nur sicher, die Schaffung all dieser Foo-Objekte zu überspringen, wenn wir zwei Dinge wüssten: dass das Aufrufen von new Foo() keine beobachtbaren Nebenwirkungen hatte; und dass keine Referenzen auf Foo den Schleifenkörper "entkamen". Ein beobachtbarer Nebeneffekt wäre etwa so, als würde man den Wert eines statischen Members setzen (z. B. wenn die Foo-Klasse eine statische Zählung aller Male beibehalten hätte, die Foo() aufgerufen wurde). Ein Beispiel für eine Referenz-Escaping wäre, wenn die this Variable innerhalb von Foo() woanders übergeben wurde.

Beachten Sie, dass es nicht reicht, nur auf Foo() zu schauen, müssen Sie auf Foo Superklasse Konstruktor (und den ganzen Weg bis die Kette zu Object). Und dann müssen Sie sich den gesamten Code ansehen, der bei der Initialisierung jedes dieser Objekte ausgeführt wird. Und dann schauen Sie sich den ganzen Code an, der von diesem Code aufgerufen wird. Das wäre eine enorme Menge an Analysen, die "Just-in-Time" zu tun haben.

public class Foo extends Bazz{ 
    static int count = 0; 

    public Foo(){ 
     // Implicit call to Bazz() has side effect 
     count++; // side effect 
     Bazz.onNewFoo(this); // reference escaping 
    } 

    Bazz bazz = new Bazz(); // side effect 
    { 
     Bazz.onNewBazz(this.bazz); // reference escaping 
    } 
} 

class Bazz{ 
    static int count = 0; 

    static List<Foo> fooList = new LinkedList<>(); 
    static List<Bazz> bazzList = new LinkedList<>(); 

    static void onNewFoo(Foo foo){ 
     fooList.add(foo); 
    } 

    static void onNewBazz(Bazz bazz){ 
     bazzList.add(bazz); 
    } 

    public Bazz(){ 
     count++; 
    } 
} 

Sie könnten denken, wir sollten javac diese Analyse und Optimierung für uns machen lassen. Das Problem dabei ist, dass es keine Möglichkeit gibt, zu garantieren, dass die Version Foo(), die zum Kompilierungszeitpunkt im Klassenpfad war, mit der Version identisch ist, die zur Laufzeit im Klassenpfad vorhanden ist. (Dies ist eine sehr nützliche Eigenschaft von Java - es erlaubt mir, meine Anwendung von Glassfish nach Tomcat zu verschieben, ohne sie neu zu kompilieren). Daher können wir der zur Kompilierungszeit durchgeführten Analyse nicht vertrauen.

Schließlich erkennen, dass String nicht anders als Foo ist. Wir müssen diese Analyse trotzdem ausführen und es gibt keine Möglichkeit, diese Analyse im Voraus durchzuführen (was ich meine JRE aktualisieren kann, ohne meine Apps neu zu kompilieren)