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 не оптимизирует его)

Другие вопросы по тегам