Declaring variables inside or outside of a loop

前端 未结 20 2047
后悔当初
后悔当初 2020-11-22 01:59

Why does the following work fine?

String str;
while (condition) {
    str = calculateStr();
    .....
}

But this one is said to be dangerou

20条回答
  •  隐瞒了意图╮
    2020-11-22 02:39

    Please skip to the updated answer...

    For those who care about performance take out the System.out and limit the loop to 1 byte. Using double (test 1/2) and using String (3/4) the elapsed times in milliseconds is given below with Windows 7 Professional 64 bit and JDK-1.7.0_21. Bytecodes (also given below for test1 and test2) are not the same. I was too lazy to test with mutable & relatively complex objects.

    double

    Test1 took: 2710 msecs

    Test2 took: 2790 msecs

    String (just replace double with string in the tests)

    Test3 took: 1200 msecs

    Test4 took: 3000 msecs

    Compiling and getting bytecode

    javac.exe LocalTest1.java
    
    javap.exe -c LocalTest1 > LocalTest1.bc
    
    
    public class LocalTest1 {
    
        public static void main(String[] args) throws Exception {
            long start = System.currentTimeMillis();
            double test;
            for (double i = 0; i < 1000000000; i++) {
                test = i;
            }
            long finish = System.currentTimeMillis();
            System.out.println("Test1 Took: " + (finish - start) + " msecs");
        }
    
    }
    
    public class LocalTest2 {
    
        public static void main(String[] args) throws Exception {
            long start = System.currentTimeMillis();
            for (double i = 0; i < 1000000000; i++) {
                double test = i;
            }
            long finish = System.currentTimeMillis();
            System.out.println("Test1 Took: " + (finish - start) + " msecs");
        }
    }
    
    
    Compiled from "LocalTest1.java"
    public class LocalTest1 {
      public LocalTest1();
        Code:
           0: aload_0
           1: invokespecial #1                  // Method java/lang/Object."":()V
           4: return
    
      public static void main(java.lang.String[]) throws java.lang.Exception;
        Code:
           0: invokestatic  #2                  // Method java/lang/System.currentTimeMillis:()J
           3: lstore_1
           4: dconst_0
           5: dstore        5
           7: dload         5
           9: ldc2_w        #3                  // double 1.0E9d
          12: dcmpg
          13: ifge          28
          16: dload         5
          18: dstore_3
          19: dload         5
          21: dconst_1
          22: dadd
          23: dstore        5
          25: goto          7
          28: invokestatic  #2                  // Method java/lang/System.currentTimeMillis:()J
          31: lstore        5
          33: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
          36: new           #6                  // class java/lang/StringBuilder
          39: dup
          40: invokespecial #7                  // Method java/lang/StringBuilder."":()V
          43: ldc           #8                  // String Test1 Took:
          45: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          48: lload         5
          50: lload_1
          51: lsub
          52: invokevirtual #10                 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
          55: ldc           #11                 // String  msecs
          57: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          60: invokevirtual #12                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
          63: invokevirtual #13                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
          66: return
    }
    
    
    Compiled from "LocalTest2.java"
    public class LocalTest2 {
      public LocalTest2();
        Code:
           0: aload_0
           1: invokespecial #1                  // Method java/lang/Object."":()V
           4: return
    
      public static void main(java.lang.String[]) throws java.lang.Exception;
        Code:
           0: invokestatic  #2                  // Method java/lang/System.currentTimeMillis:()J
           3: lstore_1
           4: dconst_0
           5: dstore_3
           6: dload_3
           7: ldc2_w        #3                  // double 1.0E9d
          10: dcmpg
          11: ifge          24
          14: dload_3
          15: dstore        5
          17: dload_3
          18: dconst_1
          19: dadd
          20: dstore_3
          21: goto          6
          24: invokestatic  #2                  // Method java/lang/System.currentTimeMillis:()J
          27: lstore_3
          28: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
          31: new           #6                  // class java/lang/StringBuilder
          34: dup
          35: invokespecial #7                  // Method java/lang/StringBuilder."":()V
          38: ldc           #8                  // String Test1 Took:
          40: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          43: lload_3
          44: lload_1
          45: lsub
          46: invokevirtual #10                 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
          49: ldc           #11                 // String  msecs
          51: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          54: invokevirtual #12                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
          57: invokevirtual #13                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
          60: return
    }
    

    UPDATED ANSWER

    It's really not easy to compare performance with all the JVM optimizations. However, it is somewhat possible. Better test and detailed results in Google Caliper

    1. Some details on blog:Should you declare a variable inside a loop or before the loop?
    2. GitHub repository: https://github.com/gunduru/jvdt
    3. Test Results for double case and 100M loop (and yes all JVM details): https://microbenchmarks.appspot.com/runs/b1cef8d1-0e2c-4120-be61-a99faff625b4

    DeclaredBefore 1,759.209 DeclaredInside 2,242.308

    • DeclaredBefore 1,759.209 ns
    • DeclaredInside 2,242.308 ns

    Partial Test Code for double Declaration

    This is not identical to the code above. If you just code a dummy loop JVM skips it, so at least you need to assign and return something. This is also recommended in Caliper documentation.

    @Param int size; // Set automatically by framework, provided in the Main
    /**
    * Variable is declared inside the loop.
    *
    * @param reps
    * @return
    */
    public double timeDeclaredInside(int reps) {
        /* Dummy variable needed to workaround smart JVM */
        double dummy = 0;
    
        /* Test loop */
        for (double i = 0; i <= size; i++) {
    
            /* Declaration and assignment */
            double test = i;
    
            /* Dummy assignment to fake JVM */
            if(i == size) {
                dummy = test;
            }
        }
        return dummy;
    }
    
    /**
    * Variable is declared before the loop.
    *
    * @param reps
    * @return
    */
    public double timeDeclaredBefore(int reps) {
    
        /* Dummy variable needed to workaround smart JVM */
        double dummy = 0;
    
        /* Actual test variable */
        double test = 0;
    
        /* Test loop */
        for (double i = 0; i <= size; i++) {
    
            /* Assignment */
            test = i;
    
            /* Not actually needed here, but we need consistent performance results */
            if(i == size) {
                dummy = test;
            }
        }
        return dummy;
    }
    

    Summary: declaredBefore indicates better performance -really tiny- and it's against the smallest scope principle. JVM should actually do this for you

提交回复
热议问题