读 Priactical Java 笔记
性能(Performance)
1. 改善java性能并不是JVM和编译器的单方责任。编写这些程序的程序员也应该尽一份力。
2. 先把焦点放在设计,数据结构和算法本身。
3. 不要倚赖编译期(compile-time)优化技术。
? 看个例子:
? Java2 SDK的 javac 相关文档宣称,- o 选项能够为运行期产生优化的代码。
? class Test
? {
? public static void main(String[] args)
? {
? int a = 10 ;
? int b = 20 ;
? int [] arr = new int[10];
? for ( int i = 0 ; i <10 ; i ++ )
? {
? arr[i] = a + b ;
? /*
a+b 这是一个不变量,所以完全可以提出到循环之外,
从而避免每次循环都要进行a+b的计算,但事实,编译器并没有为我们作这个事情。
*/
? }
? for ( int i = 0 ; i < 10 ; i ++)
? {
? System.out.print(i+"\t");
? }
? }
? }
下面让我们看看它生成的bytecod
javac -o Test.java // 带优化策略的
javap -c Test // 生成bytecod
ByteCode :
Compiled from Test.java
class Test extends java.lang.Object {
Test();
public static void main(java.lang.String[]);
}
Method Test()
0 aload_0
1 invokespecial #1 <Method java.lang.Object()>
4 return
Method void main(java.lang.String[])
0 bipush 10
2 istore_1
3 bipush 20
5 istore_2
6 bipush 10
8 newarray int
10 astore_3
11 iconst_0
12 istore 4
18 iload 4
20 iload_1
21 iload_2
22 iadd
23 iastore
24 iinc 4 1
29 bipush 10
34 iconst_0
35 istore 5
37 goto 69
40 getstatic #2 <Field java.io.PrintStream out>
43 new #3 <Class java.lang.StringBuffer>
46 dup
47 invokespecial #4 <Method java.lang.StringBuffer()>
50 iload 5
52 invokevirtual #5 <Method java.lang.StringBuffer append(int)>
55 ldc #6 <String " ">
57 invokevirtual #7 <Method java.lang.StringBuffer append(java.lang.String)>
60 invokevirtual #8 <Method java.lang.String toString()>
63 invokevirtual #9 <Method void print(java.lang.String)>
66 iinc 5 1
69 iload 5
71 bipush 10
73 if_icmplt 40
76 return
注意,[a 和b 相加]的bytecode仍在循环内部(箭头标出的循环结构)。
所以优化并没有任何作用。
4. 理解运行期(runtime)代码优化技术
JIT的目的在于将bytecode于运行期转化成本地二进制码。某些JITS在将bytecode转 化之前,对bytecode进行分析并进行优化。
5. 如欲进行字符串接合,StringBuffer优于String
测试用例:
public class StringBufferTest
{
public static void main(String[] args)
{
long start = System.currentTimeMillis();
String str ="chenfei";
for(int i = 0 ; i < 1000; i ++ )
{
str = str + "Hello!" ;
}
long end = System.currentTimeMillis();
long t1 = end - start ;
System.out.println("---------"+t1+"----------"+"\n");
long start2 = System.currentTimeMillis();
StringBuffer s = new StringBuffer("chenfei");
for(int j = 0 ; j < 1000; j ++ )
{
s = s.append(" Hello!");
}
long end2 = System.currentTimeMillis();
long t2 = end2 - start2 ;
System.out.println("----------"+t2+"---------"+"\n");
}
}
结果是:
---------78----------
----------0---------
当循环次数越多,用时差距就越大。
当i=j=10000时:
---------18781----------
----------16---------
这是为什么呢?让我们看看它的bytecode:
Compiled from StringBufferTest.java
public class StringBufferTest extends java.lang.Object {
public StringBufferTest();
public static void main(java.lang.String[]);
}
Method StringBufferTest()
0 aload_0
1 invokespecial #1 <Method java.lang.Object()>
4 return
Method void main(java.lang.String[])
0 invokestatic #2 <Method long currentTimeMillis()>
3 lstore_1
4 ldc #3 <String "chenfei">
6 astore_3
7 iconst_0
8 istore 4
10 goto 36
13 new #4 <Class java.lang.StringBuffer>
16 dup
17 invokespecial #5 <Method java.lang.StringBuffer()>
20 aload_3
21 invokevirtual #6 <Method java.lang.StringBuffer append(java.lang.String)>
24 ldc #7 <String "Hello!">
26 invokevirtual #6 <Method java.lang.StringBuffer append(java.lang.String)>
29 invokevirtual #8 <Method java.lang.String toString()>
32 astore_3
33 iinc 4 1
36 iload 4
38 sipush 10000
41 if_icmplt 13
44 invokestatic #2 <Method long currentTimeMillis()>
47 lstore 5
49 lload 5
51 lload_1
52 lsub
53 lstore 7
55 getstatic #9 <Field java.io.PrintStream out>
58 new #4 <Class java.lang.StringBuffer>
61 dup
62 invokespecial #5 <Method java.lang.StringBuffer()>
65 ldc #10 <String "---------">
67 invokevirtual #6 <Method java.lang.StringBuffer append(java.lang.String)>
70 lload 7
72 invokevirtual #11 <Method java.lang.StringBuffer append(long)>
75 ldc #12 <String "----------">
77 invokevirtual #6 <Method java.lang.StringBuffer append(java.lang.String)>
80 ldc #13 <String "
">
82 invokevirtual #6 <Method java.lang.StringBuffer append(java.lang.String)>
85 invokevirtual #8 <Method java.lang.String toString()>
88 invokevirtual #14 <Method void println(java.lang.String)>
91 invokestatic #2 <Method long currentTimeMillis()>
94 lstore 9
96 new #4 <Class java.lang.StringBuffer>
99 dup
100 ldc #3 <String "chenfei">
102 invokespecial #15 <Method java.lang.StringBuffer(java.lang.String)>
105 astore 11
107 iconst_0
108 istore 12
110 goto 125
113 aload 11
115 ldc #16 <String " Hello!">
117 invokevirtual #6 <Method java.lang.StringBuffer append(java.lang.String)>
120 astore 11
122 iinc 12 1
125 iload 12
127 sipush 10000
130 if_icmplt 113
133 invokestatic #2 <Method long currentTimeMillis()>
136 lstore 13
138 lload 13
140 lload 9
142 lsub
143 lstore 15
145 getstatic #9 <Field java.io.PrintStream out>
148 new #4 <Class java.lang.StringBuffer>
151 dup
152 invokespecial #5 <Method java.lang.StringBuffer()>
155 ldc #12 <String "----------">
157 invokevirtual #6 <Method java.lang.StringBuffer append(java.lang.String)>
160 lload 15
162 invokevirtual #11 <Method java.lang.StringBuffer append(long)>
165 ldc #10 <String "---------">
167 invokevirtual #6 <Method java.lang.StringBuffer append(java.lang.String)>
170 ldc #13 <String "
">
172 invokevirtual #6 <Method java.lang.StringBuffer append(java.lang.String)>
175 invokevirtual #8 <Method java.lang.String toString()>
178 invokevirtual #14 <Method void println(java.lang.String)>
181 return
如果你愿意看上面的Bytecode,请留意其中加红色的地方,问题的关键。
从其中我们可以知道,在字符串连接的时候,事实是创建了一个新的StringBuffer,然后调用其append方法,而创建实例是很费时的。不能用String相结合的原因在于,String是不可变得,所以只能转换成StringBuffer进行。哈哈,StringBuffer才是你进行字符串结合时的好的选择!
------待续-----
-------flyingchen
-------2005.11.10
posted on 2006-01-12 19:28 flyingchen 阅读(469) 评论(0) 收藏 举报
浙公网安备 33010602011771号