最近朋友圈里有人发了张图如下:
问朋友圈的大神有没有人能解释:为什么带“break”的双重for循环比不带“break”的循环花费的时间多。循环的次数少的却用了更长的时间?
话不多说自己动手, 代码如下:
public class CompileTest {
public static void main(String[] args) {
test1();
test2();
}
public static void test1() {
long time1 = System.currentTimeMillis();
long count1 = 0;
for (int i = 0; i < 50000; i++) {
for (int j = 0; j < 50000; j++) {
if (j > 30000) {
}
count1++;
}
}
System.err.println(System.currentTimeMillis() - time1 + "-----" + count1);
}
public static void test2() {
long time2 = System.currentTimeMillis();
long count2 = 0;
for (int i = 0; i < 50000; i++) {
for (int j = 0; j < 50000; j++) {
if (j > 30000) {
break;
}
count2++;
}
}
System.err.println(System.currentTimeMillis() - time2 + "-----" + count2);
System.err.println("==========================================");
}
}
这里为了一会儿方便看日志,用了错误打印(字体为红色)
运行之后结果如下:
结果真的是这样,带break的循环次数少,但是花费的时间却更多,这是为什么?
连续运行了好几次,都是同样的结果,这不科学啊!
如果我用for循环多运行几次呢?更改了main方法:
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
test1();
test2();
}
}
运行结果如下:
结果显示第一次循环跟之前的结果一样,第二次开始结果就不一样,循环少的运行时间就少。于是我猜测这是Java的编译机制导致的。
开始验证我的猜测:
启动参数上添加编译日志打印:-XX:+PrintCompilation
运行结果如下:
97 1 3 java.lang.Object::<init> (1 bytes)
99 2 3 java.lang.String::equals (81 bytes)
100 3 3 java.lang.String::hashCode (55 bytes)
100 4 3 java.lang.AbstractStringBuilder::ensureCapacityInternal (27 bytes)
100 5 4 java.lang.String::charAt (29 bytes)
101 6 3 java.lang.String::getChars (62 bytes)
104 7 3 java.lang.Character::toLowerCase (9 bytes)
104 12 n 0 java.lang.System::arraycopy (native) (static)
104 13 3 java.lang.String::indexOf (70 bytes)
104 14 4 java.lang.String::length (6 bytes)
105 8 3 java.lang.CharacterData::of (120 bytes)
105 9 3 java.lang.CharacterDataLatin1::toLowerCase (39 bytes)
105 10 3 java.lang.CharacterDataLatin1::getProperties (11 bytes)
105 17 3 java.io.WinNTFileSystem::isSlash (18 bytes)
106 16 3 java.lang.AbstractStringBuilder::append (29 bytes)
106 18 s 3 java.lang.StringBuffer::append (13 bytes)
107 11 3 java.lang.Math::min (11 bytes)
108 15 3 java.util.Arrays::copyOfRange (63 bytes)
109 19 3 java.lang.Character::toLowerCase (6 bytes)
110 20 3 java.lang.String::indexOf (7 bytes)
110 21 1 java.io.File::getPath (5 bytes)
110 22 3 sun.nio.cs.ext.DoubleByte$Encoder::encodeChar (21 bytes)
110 23 3 java.lang.String::toLowerCase (439 bytes)
112 24 3 java.lang.AbstractStringBuilder::append (50 bytes)
112 26 3 java.lang.StringBuilder::append (8 bytes)
112 25 1 java.net.URL::getProtocol (5 bytes)
115 27 3 java.util.HashMap::hash (20 bytes)
116 28 % 3 com.xyl.test.CompileTest::test1 @ 18 (76 bytes)
117 29 % 4 com.xyl.test.CompileTest::test1 @ 18 (76 bytes)
119 28 % 3 com.xyl.test.CompileTest::test1 @ -2 (76 bytes) made not entrant
187 29 % 4 com.xyl.test.CompileTest::test1 @ -2 (76 bytes) made not entrant
73-----2500000000
188 30 % 3 com.xyl.test.CompileTest::test2 @ 18 (95 bytes)
189 31 % 4 com.xyl.test.CompileTest::test2 @ 18 (95 bytes)
192 30 % 3 com.xyl.test.CompileTest::test2 @ -2 (95 bytes) made not entrant
637-----1500050000
==========================================
825 32 % 3 com.xyl.test.CompileTest::test1 @ 18 (76 bytes)
826 33 % 4 com.xyl.test.CompileTest::test1 @ 18 (76 bytes)
829 32 % 3 com.xyl.test.CompileTest::test1 @ -2 (76 bytes) made not entrant
53-----2500000000
878 34 4 com.xyl.test.CompileTest::test2 (95 bytes)
744-----1500050000
==========================================
1622 35 4 com.xyl.test.CompileTest::test1 (76 bytes)
48-----2500000000
1699 34 4 com.xyl.test.CompileTest::test2 (95 bytes) made not entrant
28-----1500050000
==========================================
1748 35 4 com.xyl.test.CompileTest::test1 (76 bytes) made not entrant
49-----2500000000
1748 36 3 com.xyl.test.CompileTest::test2 (95 bytes)
734-----1500050000
==========================================
2481 37 3 com.xyl.test.CompileTest::test1 (76 bytes)
47-----2500000000
2528 38 1 java.nio.Buffer::position (5 bytes)
636-----1500050000
==========================================
61-----2500000000
682-----1500050000
==========================================
48-----2500000000
3956 39 1 java.nio.Buffer::limit (5 bytes)
711-----1500050000
==========================================
65-----2500000000
704-----1500050000
==========================================
49-----2500000000
699-----1500050000
==========================================
48-----2500000000
6232 40 4 com.xyl.test.CompileTest::test2 (95 bytes)
6236 36 3 com.xyl.test.CompileTest::test2 (95 bytes) made not entrant
870-----1500050000
==========================================
7102 41 3 java.nio.ByteBuffer::arrayOffset (35 bytes)
7103 42 4 com.xyl.test.CompileTest::test1 (76 bytes)
7108 37 3 com.xyl.test.CompileTest::test1 (76 bytes) made not entrant
67-----2500000000
29-----1500050000
==========================================
48-----2500000000
29-----1500050000
==========================================
50-----2500000000
7325 43 3 java.nio.Buffer::position (43 bytes)
28-----1500050000
==========================================
7354 44 1 java.lang.Object::<init> (1 bytes)
7354 1 3 java.lang.Object::<init> (1 bytes) made not entrant
58-----2500000000
29-----1500050000
==========================================
7441 45 3 java.nio.CharBuffer::arrayOffset (35 bytes)
48-----2500000000
29-----1500050000
==========================================
48-----2500000000
30-----1500050000
==========================================
50-----2500000000
28-----1500050000
==========================================
47-----2500000000
29-----1500050000
==========================================
49-----2500000000
30-----1500050000
==========================================
49-----2500000000
35-----1500050000
==========================================
48-----2500000000
28-----1500050000
==========================================
7991 46 3 java.nio.charset.CoderResult::isUnderflow (13 bytes)
7992 47 3 java.nio.ByteBuffer::array (35 bytes)
68-----2500000000
8060 48 3 java.io.BufferedWriter::ensureOpen (18 bytes)
8060 49 1 sun.nio.cs.StreamEncoder::isOpen (5 bytes)
50-----1500050000
==========================================
当编译到上图所示的地方时,test2方法已经编译(优化)完了(因为后面的日志不再有test2方法的编译信息),也是从这里开始,后面带break循环次数少的所消耗的时间更少。
至此得出结论,如果只运行一次,Java是解释运行,所以看到的结果跟我们预期的不一样,而多次调用的时候,Java会进行编译优化(每运行一次都会收集信息,便于优化编译)。