Remainder operator on int causes java.util.Objects.requireNonNull?
Why not?
Assuming
class C {
private final int taxos = 4;
public int test() {
final int a = 7;
final int b = this.taxos;
return a % b;
}
}
a call like c.test()
where c
is declared to as C
must throw when c
is null
. Your method is equivalent to
public int test() {
return 3; // `7 % 4`
}
as you work with constants only. With test
being non-static, the check must be done. Normally, it would get done implicitly when a field gets accessed or a non-static method gets called, but you don't do it. So an explicit check is needed. One possibility is to call Objects.requireNonNull
.
The bytecode
Forget not that the bytecode is basically irrelevant for the performance. The task of javac
is to produce some bytecode whose execution corresponds with your source code. It's not meant to do any optimizations, as optimized code is usually longer and harder to analyze, while the bytecode is actually the source code for the optimizing JIT compiler. So javac
is expected to keep it simple....
The performance
In my profiler I saw there is 1% CPU spend in
java.util.Objects.requireNonNull
I'd blame the profiler first. Profiling Java is pretty hard and you can never expect perfect results.
You probably should try making the method static. You surely should read this article about null checks.
Well it seems my question was 'wrong' as it has nothing to do with the operator, but rather the field itself. Still don't know why..
public int test() {
final int a = 7;
final int b = this.taxos;
return a % b;
}
Which turns into:
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
Firstly, here's a minimal reproducible example of this behavior:
/**
* 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;
}
}
The behavior is because of how the Java compiler optimizes compile-time constants.
Note that in the byte code of foo()
no object reference is accessed to get the value of bar
. That's because it is a compile-time constant and thus the JVM can simply execute the iconst_5
operation to return this value.
When changing bar
into a non-compile time constant (either by removing the final
keyword or not initializing within declaration but inside the constructor) you would get:
/**
* 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;
}
}
where aload_0
pushes the reference of this
onto the operand stack to then get the bar
field of this object.
Here the compiler is clever enough to notice that aload_0
(the this
reference in case of member functions) can logically not be null
.
Now is your case actually a missing compiler optimization?
See @maaartinus answer.