Java >> Java tutorial >  >> Tag >> java.util

Resterende operatør på int forårsager java.util.Objects.requireNonNull?

Hvorfor ikke?

Forudsat

class C {
    private final int taxos = 4;

    public int test() {
        final int a = 7;
        final int b = this.taxos;
        return a % b;
    }
}

et opkald som c.test() hvor c er erklæret som C skal kast når c er null . Din metode svarer til

    public int test() {
        return 3; // `7 % 4`
    }

da du kun arbejder med konstanter. Med test da den ikke er statisk, skal kontrollen udføres. Normalt ville det blive gjort implicit, når et felt bliver tilgået, eller en ikke-statisk metode bliver kaldt, men du gør det ikke. Så en eksplicit kontrol er nødvendig. En mulighed er at ringe til Objects.requireNonNull .

Bytekoden

Glem ikke, at bytekoden dybest set er irrelevant for ydeevnen. Opgaven for javac er at producere nogle bytekode, hvis udførelse svarer til din kildekode. Det er ikke meningen, at det skal gøre noget optimeringer, da optimeret kode normalt er længere og sværere at analysere, mens bytekoden faktisk er kildekoden til den optimerende JIT-kompiler. Så javac forventes at holde det enkelt....

Ydeevnen

I min profiler så jeg, at der er 1 % CPU-forbrug i java.util.Objects.requireNonNull

Jeg ville give profileren skylden først. Profilering af Java er ret svært, og du kan aldrig forvente perfekte resultater.

Du skal nok prøve at gøre metoden statisk. Du bør helt sikkert læse denne artikel om nul-tjek.


Det ser ud til, at mit spørgsmål var 'forkert', da det ikke har noget at gøre med operatøren, men snarere selve feltet. Ved stadig ikke hvorfor..

   public int test() {
        final int a = 7;
        final int b = this.taxos;
        return a % b;
    }

Hvilket bliver til:

  public test()I
   L0
    LINENUMBER 51 L0
    BIPUSH 7
    ISTORE 1
   L1
    LINENUMBER 52 L1
    ALOAD 0
    INVOKESTATIC java/util/Objects.requireNonNull (Ljava/lang/Object;)Ljava/lang/Object;
    POP
    ICONST_4
    ISTORE 2
   L2
    LINENUMBER 53 L2
    BIPUSH 7
    ILOAD 2
    IREM
    IRETURN

For det første er her et minimalt reproducerbart eksempel på denne adfærd:

/**
 * OS:              Windows 10 64x
 * javac version:   13.0.1
 */
public class Test {
    private final int bar = 5;

    /**
     * public int foo();
     *   Code:
     *     0: iconst_5
     *     1: ireturn
     */
    public int foo() {
        return bar;
    }

    /**
     * public int foo2();
     *   Code:
     *     0: aload_0
     *     1: invokestatic  #13     // Method java/util/Objects.requireNonNull:(Ljava/lang/Object;)Ljava/lang/Object;
     *     4: pop
     *     5: iconst_5
     *     6: ireturn
     */
    public int foo2() {
        return this.bar;
    }
}

Adfærden skyldes, hvordan Java-kompileren optimerer kompileringstidskonstanter .

Bemærk, at i bytekoden foo() ingen objektreference er tilgået for at få værdien bar . Det er fordi det er en kompileringstidskonstant, og JVM'en kan derfor simpelthen udføre iconst_5 handling for at returnere denne værdi.

Når du ændrer bar ind i en ikke-kompilere tidskonstant (enten ved at fjerne final søgeord eller ikke initialisering i erklæringen, men inde i konstruktøren), ville du få:

/**
 * OS:              Windows 10 64x
 * javac version:   13.0.1
 */
public class Test2 {
    private int bar = 5;

    /**
     * public int foo();
     *   Code:
     *     0: aload_0
     *     1: getfield      #7
     *     4: ireturn
     */
    public int foo() {
        return bar;
    }

    /**
     * public int foo2();
     *   Code:
     *     0: aload_0
     *     1: getfield      #7
     *     4: ireturn
     */
    public int foo2() {
        return this.bar;
    }
}

hvor aload_0 skubber referencen af this på operand-stakken for derefter at få bar felt for dette objekt.

Her er compileren klog nok til at bemærke det aload_0 (den this reference i tilfælde af medlemsfunktioner) kan logisk set ikke være null .

Er din sag nu faktisk en manglende compileroptimering?

Se @maaartinus svar.


Java tag