从Java汇编来看Java程序的性能优化

原文作者为我们指出了一条提高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, 当然可能是IBMJava 编译器优化,或者是1.5 的编译器优化了(今后在证实),反正目前在ibm JRE 1.5 上编译出来的String 连接代码完全是高效的,至少目前我能放心的用了

 

©️2020 CSDN 皮肤主题: 酷酷鲨 设计师:CSDN官方博客 返回首页