JVM выполняет постоянное свертывание во время выполнения?
Если у меня есть две постоянные, известные во время компиляции, компилятор Java свернет их.
final static int foo = 2;
final static int bar = 17;
int myVariable;
int myFunction(){
return foo*bar + myVariable;
}
Во время выполнения myFunction вернет 34 + myVariable, и ему не нужно будет вычислять 2*17, как это было сделано во время компиляции.
Мой вопрос: будет ли он делать то же самое, если константы не известны до времени выполнения? Я считаю, что это называется специализация кода во время выполнения.
final int foo;
final int bar;
int myVariable;
int myFunction(){
return foo*bar + myVariable;
}
Если бы foo и bar были инициализированы как 2 и 17 в конструкторе объекта, будет ли myFunction специализирована для возврата 34 + myVariable, или она все равно будет вычислять foo*bar каждый раз, когда вызывается функция, даже если foo*bar никогда не изменится?
* Редактировать: я имею в виду новейшую версию JVM, 1.7.0_45.
3 ответа
Hotspot Скомпилированный Ассемблер
mov r10d,DWORD PTR [rsi+0x8]
shl r10,0x3
cmp rax,r10
jne 0x00007fb509045b60 ; {runtime_call}
data32 xchg ax,ax
nop WORD PTR [rax+rax*1+0x0]
nop WORD PTR [rax+rax*1+0x0]
[Verified Entry Point]
sub rsp,0x18
mov QWORD PTR [rsp+0x10],rbp ;*synchronization entry
;- Main$TestClass::myFunction@-1 (line 25)
mov eax,DWORD PTR [rsi+0x10]
imul eax,DWORD PTR [rsi+0xc]
add eax,DWORD PTR [rsi+0x14] ;*iadd
;- Main$TestClass::myFunction@13 (line 25)
add rsp,0x10
pop rbp
test DWORD PTR [rip+0x15f7a8bf],eax # 0x00007fb51f087000
;{poll_return}
ret
Java-код
public class Main {
public static void main(String... args) {
int accm = 0;
TestClass t = new TestClass(542,452);
while(true){
t.myVariable = accm;
accm += t.myFunction();
if(false) break;
}
System.out.println(accm);
}
public static class TestClass{
final int foo;
final int bar;
public int myVariable = 2;
public TestClass(int foo, int bar){
this.foo = foo;
this.bar = bar;
}
int myFunction(){
return foo*bar + myVariable;
}
}
}
Версия Java
java version "1.8.0-ea"
Java(TM) SE Runtime Environment (build 1.8.0-ea-b124)
Java HotSpot(TM) 64-Bit Server VM (build 25.0-b66, mixed mode)
Выводы
Нет, не может.
Хотя я не понимаю большей части того, что там происходит, похоже, что функция была встроена в цикл в [Verified Entry Point]
, но константы не складываются, потому что умножение все еще происходит в imul eax,DWORD PTR [rsi+0xc]
,
Foo и Bar никогда бы не изменились, в этом случае они вполне могут различаться в разных случаях, и система не компилирует код отдельно для каждого экземпляра каждого объекта. Накладные расходы от этого были бы ужасны.
Возможно, что система может обнаружить это, но это маловероятно, и вы, конечно, не могли ожидать, что все или даже большинство сред сделают это. Ваш лучший выбор будет хранить foobar = foo*bar
сами и используйте это, если вы думаете, что умножение будет иметь значительное влияние на производительность. (Что маловероятно).
У меня есть JDK 1.7.0_45, и я скомпилировал этот код
class X {
final int foo;
final int bar;
X() {
foo = 2;
bar = 17;
}
int myVariable;
int myFunction() {
return foo * bar + myVariable;
}
и получил этот bytecote для myFunction
myFunction()I
L0
LINENUMBER 22 L0
ALOAD 0
GETFIELD test/X.foo : I <-- load 2
ALOAD 0
GETFIELD test/X.bar : I <-- load 17
IMUL <-- 2 x 17
ALOAD 0
GETFIELD test/X.myVariable : I
IADD
IRETURN
так ответьте "будет ли он вычислять foo*bar каждый раз, когда вызывается функция?" да (если JVM не оптимизирует его)