jdk versus openjdk - Optimierung von Konstanten?

Sn0wm4n

Mitglied
Hallo zusammen,

im Folgenden zeige ich Euch den Quelltext zu einem Mini-Programm, das mir ziemlich rätselhaft vorkommt:
Java:
public class InfiniteLoop {

	public static final int END = Integer.MAX_VALUE;
	public static final int START = END - 100;
	
	public static void main (String[] args) {
		
		int count = 0;
		for (int i = START; i <= END; i++) {
			count++;
		}
		
		System.out.println(count);
	}
}

Kurze Erklärung des Source:
Im Prinzip wird einfach eine Schleife von START bis END durchlaufen und sodann die Anzahl der Durchläufe ausgegeben - im Allgemeinen kein Problem.
Da nun aber END auf den maximal möglichen Wert eines 4Byte signed Integer gesetzt wird und die Schleife erst bei i > END abbricht (da i <= END), kommt es zu einer Endlos-Schleife. Völlig korrekt - der Integer i muss theoretisch im "letzten" Schleifendurchlauf um 1 größer als der maximal mögliche Integer sein, was nicht möglich ist. Stattdessen wird i klammheimlich umgebogen auf Integer.MIN_VALUE. Daher wird die Schleife nie beendet.
So auch das von mir erwartete Ergebnis.

Das Problem:
Ich habe die Start- und Endwerte explizit als Konstanten deklariert, auch wenn das eigentlich nicht nötig wäre, um eine Endlosschleife zu erzeugen.
Mit jre6 wie auch mit java-6-openjdk als Standard-VM funktioniert das Programm auch wie erwartet (Endlosschleife).
Aaaber: Nun entferne ich den final-Modifizierer von END. Dadurch sollte logischer Weise (nach meiner Logik 😀) das Ergebnis nicht verändert werden. Für die sun/oracle-Variante stimmt das auch. Unter Verwendung der freien Variante wird nun jedoch die Zahl "101" ausgegeben - die Schleife terminiert also anscheinend.

Warum genau nun via openjdk die Schleife terminiert ist mir ein Rätsel. Irgendwas muss intern anders laufen, sobald ich den final-Modifizierer entferne. Allerdings sollte das nach meinem Verständnis keine Auswirkung haben; die Zählvariable i bleibt ja trotzdem gleich.

Daraufhin habe ich mir Interesse halber den Bytecode der beiden Quelltexte via javap "disassembled". Die Ausgaben habe ich als txt-Datei diesem Thread angehängt. Aber auch da sehe ich nichts, was die Ursache der Änderung sein könnte.

Die eigentliche Frage:
Hat von Euch einer eine Ahnung oder Vermutung, was hier im openjdk anders behandelt wird (wenn das überhaupt die Ursache ist), sodass obiges Problem auftritt (evtl. irgendeine Art von automatischer "Optimierung")?
Theoretisch wäre das Problem gar nicht mal so wichtig, da es sich um einen reinen corner-case handelt und man in solchen Randfällen sicher keine Integer-Zählvariable nutzen würde. Aber trotzdem interessiert es mich einfach, warum die verschiedenen VMs in diesem Punkt völlig anders reagieren.
Entweder sehe ich vor lauter Bäumen den Wald nicht mehr oder es ist wirklich ein sehr komisches Problem.


Mein System:
Es sollte eigentlich dabei weniger eine Rolle spielen, aber der Vollständigkeit halber:

System mit openjdk
  • 32-bit Unix (Ubuntu 10.04)
  • Eclipse 1.2.2.x zur Entwicklung

Referenz-System ohne openjdk
  • 64-bit Windows (Win7)
  • Eclipse 1.2.2.x zur Entwicklung

Falls ich noch etwas vergessen habe, einfach melden.

Vielen Dank im Voraus, dass Ihr schonmal bis hier hin gelesen habt. 😉
Und noch ein größeres Dankeschön für Hilfe!

viele Grüße,
Sn0wm4n
 

Anhänge

Ich tippe auf ne optimierung (inner jvm) die bemerkt das du die schleife 101 mal ausführst ( wenn es keine integergrenze gäbe)
also packt die das ganze zum optmieren einfach 101 ma hinterinander, und optimiert dann wahrschenlich die 101 count ++ zu einem count = 101 um.

Effektiv ist das also ein Bugfeature, da die optimierung nicht prüft, ob die 101 mal über haupt mit dem Werteberech möglich sind.
 
Hallo Empire Phoenix,

danke für die schnelle Antwort.
Dann werde ich mal nach genauen Spezifikationen zu den Optimierungen beider jvm suchen (interessiert mich noch, wo / wie / wann genau das Programm "kaputt optimiert" wird^^) und mich evtl damit direkt an die openjdk'ler wenden.

viele Grüße,
Sn0wm4n
 
Spannend wäre jetzt: Was passiert, wenn in der Schleife noch ein System.out.println(count) steht...

Ups, ganz vergessen, src. Das Verhalten hatte ich auch schon getestet.
Sobald ich via openjdk das final entferne und ein belibiges SysOut in der Schleife einfüge, terminiert diese wieder nicht.

Hast Du eine Ahnung, warum dies so ist?
 
Das würde die Vermutung von Empire Phoenix unterstützen: Er erkennt, dass dort effektiv nichts anderes gemacht wird als 100 mal count++, und rechnet das Ergebnis direkt aus... Man könnte noch ein bißchen rumprobieren, und mal versuchen, ihn mit dem Hochzählen eines long-Wertes länger zu beschäftigen. Wenn das JDK dann z.B. 5 Sekunden braucht, um von 0 bis Long.MAX_VALUE zu zählen, und das OpenJDK es in 0.00001 Sekunden schafft, kann man sicher sein, dass es NICHT wirklich gezählt hat... 😉
 
So schlau ist open-jdk anscheinend nicht 🙂
Meine Schleife von 0 bis zum max. Long-Wert läuft nun schon mehrere Minuten - wird (ohne sysout) wohl nicht optimiert.
Ich werde - voraussichtlich am Wochenende - noch ein wenig herumtesten, unter welchen Bedinungen optimiert wird. Für heute (naja, mittlerweile ists ja schon nach Mitternacht) reicht es erstmal *g

viele Grüße und Danke für die Antworten,
Stefan
 

Zurück
Oben