<!-- [if gte mso 9]><xml> <w:WordDocument> <w:View>Normal</w:View> <w:Zoom>0</w:Zoom> <w:PunctuationKerning/> <w:DrawingGridVerticalSpacing>7.8 pt</w:DrawingGridVerticalSpacing> <w:DisplayHorizontalDrawingGridEvery>0</w:DisplayHorizontalDrawingGridEvery> <w:DisplayVerticalDrawingGridEvery>2</w:DisplayVerticalDrawingGridEvery> <w:ValidateAgainstSchemas/> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:Compatibility> <w:SpaceForUL/> <w:BalanceSingleByteDoubleByteWidth/> <w:DoNotLeaveBackslashAlone/> <w:ULTrailSpace/> <w:DoNotExpandShiftReturn/> <w:AdjustLineHeightInTable/> <w:BreakWrappedTables/> <w:SnapToGridInCell/> <w:WrapTextWithPunct/> <w:UseAsianBreakRules/> <w:DontGrowAutofit/> <w:UseFELayout/> </w:Compatibility> <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel> </w:WordDocument> </xml><![endif]--><!-- [if gte mso 9]><xml> <w:LatentStyles DefLockedState="false" LatentStyleCount="156"> </w:LatentStyles> </xml><![endif]--><!-- [if gte mso 10]> <style> /* Style Definitions */ table.MsoNormalTable {mso-style-name:"Table Normal"; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:"Times New Roman"; mso-fareast-font-family:"Times New Roman"; mso-ansi-language:#0400; mso-fareast-language:#0400; mso-bidi-language:#0400;} </style> <![endif]-->
原文作者为我们指出了一条提高Java 性能的有效方法:使用Java 汇编,但是如何权衡效率与其他软件性能(如可维护性、扩展性)等等,需要读者根据项目需要做出取舍。
这些天一直在用Oolong 学习Java 汇编, 也试图反编译一些java 代码。在这之间突然意识到原来自己过去犯了很多编程的误区,导致代码效率低下。这里列出一些需要注意的地方
1. 尽量避免使用iterator (不是必要的话)
原来自己看C++ 的书,看到STL 里的iterator 的用法,很多书上说这种用法好,如果实现一变,iterator 的代码可以不用修改。我是一个很信书的人,自从那时候开始,我就喜欢上用iterator 了,比如以下代码(第一段)
for(int i=0;i<results.size();i++){
String result = results.get(i);
}
都换成了(第二段)
Iterator<String> iter =
results.iterator();
while(iter.hasNext()){
String result = iter.next();
}
最近学习Oolong, 反编译这两段代码,妈妈啊
第一段的Java 汇编是
.line 11
l9: iconst_0
l10: istore 5
l12: iload 5
l14: aload 4
l16: invokevirtual java/util/ArrayList/size ()I
l19: if_icmpge l40
.line 12
l22: aload 4
l24: iload 5
l26: invokevirtual java/util/ArrayList/get
(I)Ljava/lang/Object;
l29: checkcast java/lang/String
l32: astore 6
.line 11
l34: iinc 5 1
l37: goto l12
第二段的Java
汇编是
.line 15
l40: aload 4
l42: invokevirtual java/util/ArrayList/iterator
()Ljava/util/Iterator;
l45: astore 5
.line 16
l47: aload 5
l49: invokeinterface java/util/Iterator/hasNext ()Z 1
l54: ifeq l72
.line 17
l57: aload 5
l59: invokeinterface java/util/Iterator/next
()Ljava/lang/Object; 1
l64: checkcast java/lang/String
l67: astore 6
.line 18
l69: goto l47
仔细一看,前一段用的是invokevirtual
,后面那段用的是invokeinterface
,原理上来说invokeinterface
的速度比 invokevirtual
要慢得多,测试两段代码发现,后一段代码耗时比前一段多一个数量级,以后千万不要乱用iterator
了
二如果没有必要,尽量不要使用接口
原来写程序的时候,迷信类型分装,结果不分青红皂白,返回对象清一色的接口
比如一个返回中间结果的方法变成了这样:
public List<String> getList(){
List<String> results = new ArrayList<String>();
return results;
}
处理的方法象这样
public void handle(List<String> contents){
for(int index=0;index<contents.size();index++){
String content = contents.get(index);
}
}
反编译handle 发现
.line 9
l0: iconst_0
l1: istore_2
l2: iload_2
l3: aload_1
l4: invokeinterface java/util/List/size ()I 1
l9: if_icmpge l29
.line 10
l12: aload_1
l13: iload_2
l14: invokeinterface java/util/List/get (I)Ljava/lang/Object;
2
l19: checkcast java/lang/String
l22: astore_3
.line 9
l23: iinc 2 1
l26: goto l2
哎哟,又是invokeinterface
,赶紧的改回来
public void handle(ArrayList<String>
contents){
for(int index=0;index<contents.size();index++){
String content = contents.get(index);
}
}
public ArrayList<String> getList(){
ArrayList<String> results = new ArrayList<String>();
return results;
}
这会好了,不在是invokeinterface
,是invokevirtual
,快啊
.line 9
l0: iconst_0
l1: istore_2
l2: iload_2
l3: aload_1
l4: invokevirtual java/util/ArrayList/size ()I
l7: if_icmpge l25
.line 10
l10: aload_1
l11: iload_2
l12: invokevirtual java/util/ArrayList/get (I)Ljava/lang/Object;
l15: checkcast java/lang/String
l18: astore_3
.line 9
l19: iinc 2 1
l22: goto l2
三. 1.5
的新特性,
小心使用
看到1.5 的新特性,好用啊,这个
for(String content:contents){
}
反编译,怎么还是iterator?, 小心, 别乱用哦
l0: aload_1
l1: invokevirtual java/util/ArrayList/iterator
()Ljava/util/Iterator;
l4: astore_2
l5: aload_2
l6: invokeinterface java/util/Iterator/hasNext ()Z 1
l11: ifeq l27
l14: aload_2
l15: invokeinterface java/util/Iterator/next
()Ljava/lang/Object; 1
l20: checkcast java/lang/String
l23: astore_3
.line 10
l24: goto l5
四.1.5
中的String
不再是可怕的性能瓶颈啦
在effective java 里曾经说过,尽量少用String 做连接运算,做字符串连接运算时,StringBuffer 性能更好,可以减少new String 的次数
可是在1.5 里尝试以下代码
String contents = "";
for(int i=0;i<10000;i++)
contents += "Hello";
结果反编译一看,完全不是那么回事
.line 9
l0: ldc ""
l2: astore_1
.line 10
l3: iconst_0
l4: istore_2
l5: iload_2
l6: sipush 10000
l9: if_icmpge l38
.line 11
l12: new java/lang/StringBuilder
l15: dup
l16: invokespecial java/lang/StringBuilder/<init> ()V
l19: aload_1
l20: invokevirtual java/lang/StringBuilder/append
(Ljava/lang/String;)Ljava/lang/StringBuilder;
l23: ldc "Hello"
l25: invokevirtual java/lang/StringBuilder/append
(Ljava/lang/String;)Ljava/lang/StringBuilder;
l28: invokevirtual java/lang/StringBuilder/toString
()Ljava/lang/String;
l31: astore_1
.line 10
l32: iinc 2 1
l35: goto l5
这里用的就是StringBuffer
的替代品StringBuilder,
当然可能是IBM
的Java
编译器优化,或者是1.5
的编译器优化了(今后在证实),反正目前在ibm JRE1.5
上编译出来的String
连接代码完全是高效的,至少目前我能放心的用了