Рассчитываются ли арифметические операции над литералами во время компиляции или во время выполнения?

У меня есть следующее:

double timeInMinutes = (double) timeInMilliseconds / (1000 * 60);

Является ли операция (1000 * 60) сделано во время компиляции или во время выполнения? Другими словами, существуют ли различия в производительности во время выполнения между фрагментом кода выше и:

double timeInMinutes = (double) timeInMilliseconds / 60000;

РЕДАКТИРОВАТЬ: мой вопрос отличается от того, будет ли компилятор Java пересчитать суммы литералов?, так как я смешиваю использование переменных и литералов в арифметических операциях. Это небольшая разница, но, как заметил @TagirValeev в комментариях ( арифметические операции над литералами рассчитываются во время компиляции или во время выполнения?), Есть случаи, когда некоторые литералы не компилируются заранее, даже если бы они могли быть.

3 ответа

Согласно JLS §15.2 - Формы выражений

Некоторые выражения имеют значение, которое может быть определено во время компиляции. Это постоянные выражения (§15.28).

Мультипликативные операторы, такие как *, /, and % подпадает под постоянные выражения, поэтому он будет определен во время компиляции.

@SergeyMorozov был быстрее, чем я, чтобы написать и получить доказательство байтового кода (#2 = Integer 60000) но вот практическое доказательство и выше теоретическое / официальное утверждение:

Попробуйте также сгенерировать байт-код на вашем конце, используя 1000 * 60 а также 60000, и вы увидите те же инструкции байт-кода, и, следовательно, будет одинаковая производительность во время выполнения.

Класс Java:

public class Test {
    public static void main(String[] args) {
        int compileTest = 1000 * 60;
    }
}

Байт-код:

Classfile /E:/Test.class
  Last modified Oct 9, 2015; size 265 bytes
  MD5 checksum fd115be769ec6ef7995e4c84f7597d67
  Compiled from "Test.java"
public class Test
  SourceFile: "Test.java"
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #4.#13         //  java/lang/Object."<init>":()V
   #2 = Integer            60000
   #3 = Class              #14            //  Test
   #4 = Class              #15            //  java/lang/Object
   #5 = Utf8               <init>
   #6 = Utf8               ()V
   #7 = Utf8               Code
   #8 = Utf8               LineNumberTable
   #9 = Utf8               main
  #10 = Utf8               ([Ljava/lang/String;)V
  #11 = Utf8               SourceFile
  #12 = Utf8               Test.java
  #13 = NameAndType        #5:#6          //  "<init>":()V
  #14 = Utf8               Test
  #15 = Utf8               java/lang/Object
{
  public Test();
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0

  public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=2, args_size=1
         0: ldc           #2                  // int 60000
         2: istore_1
         3: return
      LineNumberTable:
        line 3: 0
        line 4: 3
}

Во время компиляции. Это одна из самых простых оптимизаций компилятора, известная как Constant Folding.

Просто создайте класс Test

public class Test {
    public static void main(String [] args) {
        long timeInMilliseconds = System.currentTimeMillis();
        double timeInMinutes = (double) timeInMilliseconds / (1000 * 60);
        System.out.println(timeInMinutes);
    }
}

и декомпилируем его с помощью команды: javap -v Test

Вы можете увидеть вывод декомпилированного класса:

  public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
  stack=4, locals=5, args_size=1
     0: invokestatic  #2                  // Method java/lang/System.currentTimeMillis:()J
     3: lstore_1
     4: lload_1
     5: l2d
     6: ldc2_w        #3                  // double 60000.0d
     9: ddiv
    10: dstore_3
    11: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
    14: dload_3
    15: invokevirtual #6                  // Method java/io/PrintStream.println:(D)V
    18: return
  LineNumberTable:
    line 3: 0
    line 4: 4
    line 5: 11
    line 6: 18

Посмотрите на строку 6: ldc2_w # 3 // double 60000.0d

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