Seltsame Bytecode-Größe

me-ton

Mitglied
Hi folks,

bitte betrachtet folgende Codefragmente:

A)
Java:
boolean aMethodReturningABoolean() {
    getDoubleA() >= 50 && getDoubleB() >= 0.01;
}

B)
Java:
boolean aMethodReturningABoolean() {
    getDoubleA() > 50 && getDoubleB() > 0.01;
}

Anders als vermutet ist der Bytecode nach dem Kompilieren von B) zwei Byte größer als A).

Woher kommt das?
 
Ich wundere mich, dass du dir über solche Fragen Gedanken machst.

Ich habe nicht in die Spezifikation geschaut und habe dazu auch keine Lust. Aber möglicherweise wird "greater or equals" im Bytecode durch "ge" abgekürzt und "greater than" durch "gth" (oder ähnliches). Das würde die Differenz jedenfalls erklären.
 
Erst einmal ist dieser Code nicht übersetzbar, denn Du da zeigst.

Und bei einem Test einer Klasse macht es keinen Unterschied in der Größe:
Java:
public class Test {
  double a, b;

  double getA() { return a; }
  double getB() { return b; }

  public Test(double a, double b) {
    this.a = a;
    this.b = b;
  }

  public boolean test() {
    return getA() >= 50 && getB() >= 0.01;
  }
}

Einmal so und einmal nur mit > übersetzt und beides Mal hat die Test.class die gleiche Größe (510 Bytes bei mir).

Ich wundere mich, dass du dir über solche Fragen Gedanken machst.

Ich habe nicht in die Spezifikation geschaut und habe dazu auch keine Lust. Aber möglicherweise wird "greater or equals" im Bytecode durch "ge" abgekürzt und "greater than" durch "gth" (oder ähnliches). Das würde die Differenz jedenfalls erklären.
In Bytecode dürfte das doch if_cmpge (0xa2) und if_cmpgt (0xa3) sein. Oder habe ich da jetzt etwas durcheinander geworfen?
 
Der Compiler wird wohl so schlau sein und so etwas einfaches wegoptimieren...

Ich lüge nicht falls du das meintest.
Was soll der Compiler Weg optimieren bei meinem Beispiel?

Und niemand sagt, dass du lügst. Nur dass der gezeigte Code nicht compiliert.

Bring etwas, das man nachvollziehen kann, dann kann man sich das im Detail ansehen. So hat man schlicht nichts, das man ansehen könnte.
 
In Bytecode dürfte das doch if_cmpge (0xa2) und if_cmpgt (0xa3) sein. Oder habe ich da jetzt etwas durcheinander geworfen?
Die Betonung meines Beitrags sollte darauf liegen, dass ich die Beschäftigung mit solchen Fragen lächerlich finde. Ich intereesier mich nicht dafür, wie mein Java-Code in Byte-Code übersetzt wird. Und noch wenige kümmere ich mich um zwei (in Zahlen: 2) Bytes.
 
Um konkret zu werden, basierend auf dem Beispiel @kneitzel:

Java:
    public boolean testGreater() {
        return getA() > 50 && getB() > 0.01;
    }
    
    public boolean testGreaterThen() {
        return getA() >= 50 && getB() >= 0.01;
    }

Code:
    public boolean testGreater();
        Flags: PUBLIC
        Code:
                  linenumber      10
               0: aload_0         /* this */
               1: invokevirtual   Test.getA:()D
               4: ldc2_w          50.0
               7: dcmpl          
               8: ifle            24
              11: aload_0         /* this */
              12: invokevirtual   Test.getB:()D
              15: ldc2_w          0.01
              18: dcmpl          
              19: ifle            24
              22: iconst_1       
              23: ireturn        
              24: iconst_0       
              25: ireturn        
        StackMapTable: 00 01 18
    
    public boolean testGreaterThen();
        Flags: PUBLIC
        Code:
                  linenumber      14
               0: aload_0         /* this */
               1: invokevirtual   Test.getA:()D
               4: ldc2_w          50.0
               7: dcmpl          
               8: iflt            24
              11: aload_0         /* this */
              12: invokevirtual   Test.getB:()D
              15: ldc2_w          0.01
              18: dcmpl          
              19: iflt            24
              22: iconst_1       
              23: ireturn        
              24: iconst_0       
              25: ireturn        
        StackMapTable: 00 01 18

Und wir sehen, sind ident mit Ausnahme des Vergleichs. Also was auch immer den Unterschied bei dir ausloest, wird sich vermutlich nicht in der Methode finden.

Im Zweifelsfall, schnapp' dir einen Decompiler wie JD oder Luyten, und schaue nach was der Unterschied ist.
 
Es kommt auf die Version vom Compiler - und vermutlich noch einigen weiteren Heuristiken an. Einige (JDK 18 ea build 16) generieren sowas:
Code:
 0: aload_0
 1: invokevirtual #7                  // Method getA:()D
 4: ldc2_w        #13                 // double 50.0d
 7: dcmpl
 8: iflt          26
11: aload_0
12: invokevirtual #15                 // Method getB:()D
15: ldc2_w        #18                 // double 0.01d
18: dcmpl
19: iflt          26
22: iconst_1
23: goto          27
26: iconst_0
27: ireturn
andere (wie @Robert Zenz zeigte) eben kürzere Sequenzen mit conditional jumps und mehreren returns.
 
Die Betonung meines Beitrags sollte darauf liegen, dass ich die Beschäftigung mit solchen Fragen lächerlich finde.
Da ist die Frage, was das jeweilige Interesse ist. Wenn man ein Auto nur fahren will, dann ist es komplett egal, wie der Motor des Autos genau gesteuert wird und die Software des Computers interessiert einen nicht. Aber dennoch kann es einen interessieren und es muss auch Leute geben, die sich gut damit auskennen - weil die sowas halt bauen.
Und das mit dem Java Bytecode ist durchaus interessant. Bei Java hat mich das so im Detail auch noch nicht interessiert, aber bei .Net habe ich mich sehr intensiv mit MSIL auseinander gesetzt gehabt ...

@Robert Zenz Danke. Und da erkennt man auch gleich einen Fehler - das icmp<op> ist natürlich falsch - es muss das dcmp<op> sein. Und interessamte weise gibt es da als Operatoren nur "g" und "l" und kein "ge" oder "le".
 
Jo, weil ge = !l und le = !g
Ja, das ist die Übersetzung. Nur eben wurde das bei int Werten nicht gemacht:

if_icmp<cond>​

Operation​

Branch if int comparison succeeds

Format​


if_icmp<cond>
branchbyte1
branchbyte2

Forms​

if_icmpeq = 159 (0x9f)
if_icmpne = 160 (0xa0)
if_icmplt = 161 (0xa1)
if_icmpge = 162 (0xa2)
if_icmpgt = 163 (0xa3)
if_icmple = 164 (0xa4)

Da ich nun einmal erst über die int Version gestolpert bin, ist mir dies halt aufgefallen.
 
Ich konnte anhand des von kneitzel gezeigten Beispiel das Verhalten gerade nicht reproduzieren... In "echt" erstellte ich zusätzlich ein jar file, das weist die unterschiedlichen Größen auf...

Was machst du dann hier in dem Thema? Trollen? Wenn es dich nicht interessiert dann ist doch einfach weggehen die beste Sache, oder etwa nicht?
streitet euch bitte nicht meinetwegen...
 
Kannst du ggf. ein Beispiel aufzeigen? Also ggf ein Projekt mit paar Dateien, an Hand dessen sich das reproduzieren lässt? Denn mich würde das schon interessieren.
 
Ein jar File nutzt das ZIP Format. Daher ist es komprimiert.
jar bzw. zip heisst nicht automatisch "komprimiert". zip ist ja nur ein Containerformat wie tar, welches optional auch Komprimierung unterstützt. Natürlich nutzen die meisten zip/jar Tools hier eine Compression Ratio != 0 bei zip. Man kann aber auch zip-Dateien wie tar behandeln mit Compression ratio = 0.
Tatsächlich ist es mittlerweile auch üblich, jar Dateien nicht mehr zu komprimieren, um das Laden der Klassen zur Laufzeit zu beschleunigen, gerade wenn Bandbreite keine Rolle mehr spielt.
Siehe auch https://docs.oracle.com/javase/tutorial/deployment/jar/build.html :
You might want to avoid compression, for example, to increase the speed with which a JAR file could be loaded by a browser. Uncompressed JAR files can generally be loaded more quickly than compressed files because the need to decompress the files during loading is eliminated. However, there is a tradeoff in that download time over a network may be longer for larger, uncompressed files.
 

Neue Themen


Zurück
Oben