从CMMI规范的角度看Java性能优化

随着CMMI体系结构的推广,对于编码方面的语言的使用和规范上也有了统一的要求。在Java的编码风格、格式、规范等,已经在《编码规范》的Java部分进行了详细的定义,同时,也包括一些对性能优化方面的建议,最后结合《编码检查单》对相应的Java的格式、规范以及性能方面进行检查。但是,《编码规范》在性能优化方面的建议虽然有却不是很多,下面将结合Java编写经验以及参考实际项目组在系统优化方面的建议针对Java的编程方面提出一些优化方面的建议。

 

1、慎用异常

异常对性能不利。抛出异常首先要创建一个新的对象。Throwable接口的构造函数调用名为fillInStackTrace()的本地方法,fillInStackTrace()方法检查堆栈,收集调用跟踪信息。只要有异常被抛出,VM就必须调整调用堆栈,因为在处理过程中创建了一个新的对象。

异常只能用于错误处理,不应该用来控制程序流程。

2、不要重复初始化变量

默认情况下,调用类的构造函数时,Java会把变量初始化成确定的值:所有的对象被设置成null,整数变量(byte、short、int、long)设置成0,float和double变量设置成0.0,逻辑值设置成false。当一个类从另一个类派生时,这一点尤其应该注意,因为用new关键词创建一个对象时,构造函数链中的所有构造函数都会被自动调用。

3、尽量指定类的final修饰符

带有final修饰符的类是不可派生的。在Java核心API中,有许多应用final的例子,例如java.lang.String。为String类指定final防止了人们覆盖length()方法。

另外,如果指定一个类为final,则该类所有的方法都是final。Java编译器会寻找机会内联所有的final方法(这和具体的编译器实现有关)。此举能够使性能平均提高50%。

4、尽量使用局部变量

调用方法时传递的参数以及在调用中创建的临时变量都保存在栈中,速度较快。其他变量,如静态变量、实例变量等,都在堆中创建,速度较慢。另外,依赖于具体的编译器,局部变量还可能得到进一步优化。

5、使用移位操作代替乘法和除法

符号性质的乘除是一个很“昂贵”的操作,使用移位操作将会更快更有效

考虑下面的代码:

for (val = 0; val<100000; val+=5)

 {

alterX = val * 8;

myResult = val * 2;

}

用移位操作替代乘法操作可以极大地提高性能。下面是修改后的代码:

for (val = 0; val <100000; val+= 5)

{

 alterX = val <<3;

myResult = val <<1;

}

修改后的代码不再做乘以8的操作,而是改用等价的左移3位操作,每左移1位相当于乘以2。相应地,右移1位操作相当于除以2。值得一提的是,虽然移位操作速度快,但可能使代码比较难于理解,所以最好加上一些注释。

但是使用移位操作这个方法是一把双刃剑,除非是在一个非常大的循环内,性能非常重要,而且你很清楚你自己在做什么,使用这种方法比较合算。否则提高性能所带来的程序晚读性的降低将是不合算的。

6、避免在循环条件中使用复杂表达式

在不做编译优化的情况下,在循环中,循环条件会被反复计算,如果不使用复杂表达式,而使循环条件值不变的话,程序将会运行的更快。

例子:

import java.util.Vector;

class CEL {

     voidmethod (Vector vector) {

        for (int i = 0; i < vector.size (); i++) 

            ; // 执行操作

     }

}

更正:

class CEL_fixed {

     voidmethod (Vector vector) {

        int size = vector.size ()

        for (int i = 0; i < size; i++)

            ; // 执行操作

     }

}

7、使用finally释放资源

程序中使用到的资源应当被释放,以避免资源泄漏。这最好在finally块中去做。不管程序执行的结果如何,finally块总是会执行的,以确保资源的正确关闭。

例子:

import java.io.*;

public class CS {

     publicstatic void main (String args[]) {

        CS cs = new CS ();

        cs.method ();

     }

     publicvoid method () {

        try {

            FileInputStream fis = new FileInputStream ("CS.java");

            int count = 0;

            while (fis.read () != -1)

                count++;

            System.out.println (count);

            fis.close ();

        } catch (FileNotFoundException e1) {

        } catch (IOException e2) {

        }

     }

}

更正:

在最后一个catch后添加一个finally块。

8、设置访问实例内变量的getter/setter方法为final

简单的getter/setter方法应该被置成final,这会告诉编译器,这个方法不会被重载,所以,可以变成”inlined”
    例子:

class MAF {

     publicvoid setSize (int size) {

        _size = size;

     }

     privateint _size;

}

更正:

class DAF_fixed {

     finalpublic void setSize (int size) {

        _size = size;

     }

     privateint _size;

}

9、避免不需要的instanceof操作

如果左边的对象的静态类型等于右边的,instanceof表达式返回永远为true。

例子:

public class UISO {

     publicUISO () {}

}

class Dog extends UISO {

     void method(Dog dog, UISO u) {

        Dog d = dog;

        if (d instanceof UISO) // always true.

            System.out.println("Dog is a UISO");

        UISO uiso = u;

        if (uiso instanceof Object) // always true.

            System.out.println("uiso is an Object");

     }

}

更正:

删掉不需要的instanceof操作。

class Dog extends UISO {

     voidmethod () {

        Dog d;

        System.out.println ("Dog is an UISO");

        System.out.println ("UISO is an UISO");

     }

}

10、少用new关键词创建类的实例

用new关键词创建类的实例时,构造函数链中的所有构造函数都会被自动调用。但如果一个对象实现了Cloneable接口,我们可以调用它的clone()方法。clone()方法不会调用任何类构造函数。

在使用设计模式(Design Pattern)的场合,如果用Factory模式创建对象,则改用clone()方法创建新的对象实例非常简单。例如,下面是Factory模式的一个典型实现:

public static CreditgetNewCredit() {

return new Credit();

}

改进后的代码使用clone()方法,如下所示:

private static Credit BaseCredit =new Credit();

public static CreditgetNewCredit() {

return (Credit)BaseCredit.clone();

}

上面的思路对于数组处理同样很有用。

11、避免不需要的造型操作

所有的类都是直接或者间接继承自Object。同样,所有的子类也都隐含的“等于”其父类。那么,由子类造型至父类的操作就是不必要的了。

例子:

class UNC {

     String_id = "UNC";

}

class Dog extends UNC {

     voidmethod () {

        Dog dog = new Dog ();

        UNC animal = (UNC)dog;   // not necessary.

        Object o = (Object)dog;         // notnecessary.

     }

}

更正:

class Dog extends UNC {

     voidmethod () {

        Dog dog = new Dog();

        UNC animal = dog;

        Object o = dog;

     }

}

12、不要在循环中调用synchronized(同步)方法

方法的同步需要消耗相当大的资料,在一个循环中调用它绝对不是一个好主意。

例子:

import java.util.Vector;

public class SYN {

     publicsynchronized void method (Object o) {

     }

     privatevoid test () {

        for (int i = 0; i < vector.size(); i++) {

            method (vector.elementAt(i));     // violation

        }

     }

     privateVector vector = new Vector (5, 5);

}

更正:

不要在循环体中调用同步方法,如果必须同步的话,推荐以下方式:

import java.util.Vector;

public class SYN {

     publicvoid method (Object o) {

     }

private void test () {

    synchronized{//在一个同步块中执行非同步方法

            for (int i = 0; i < vector.size(); i++) {

                method (vector.elementAt(i));  

            }

        }

     }

     privateVector vector = new Vector (5, 5);

}

13、将try/catch块移出循环

把try/catch块放入循环体内,会极大的影响性能,性能会有明显的下降。

例子:

import java.io.FileInputStream;

public class TRY {

     voidmethod (FileInputStream fis) {

        for (int i = 0; i < size; i++) {

            try{                                      // violation

                _sum += fis.read();

            } catch (Exception e) {}

        }

     }

     privateint _sum;

}

更正:

将try/catch块移出循环

void method (FileInputStream fis){

        try {

            for (int i = 0; i < size; i++) {

                _sum += fis.read();

            }

        } catch (Exception e) {}

     }

14、对于常量字符串,用String代替StringBuffer

常量字符串并不需要动态改变长度。

例子:

public class USC {

     Stringmethod () {

        StringBuffer s = new StringBuffer ("Hello");

        String t = s + "World!";

        return t;

     }

}

更正:

把StringBuffer换成String,如果确定这个String不会再变的话,这将会减少运行开销提高性能。

15、不要在循环体中实例化变量

在循环体中实例化临时变量将会增加内存消耗

例子:

import java.util.Vector;

public class LOOP{

     voidmethod (Vector v) {

        for (int i=0;i < v.size();i++) {

            Object o = new Object();

            o = v.elementAt(i);

        }

     }

}

更正:

在循环体外定义变量,并反复使用 

import java.util.Vector;

public class LOOP{

     voidmethod (Vector v) {

        Object o;

        for (int i=0;i<v.size();i++) {

            o = v.elementAt(i);

        }

     }

}

16、确定StringBuffer的容量

StringBuffer的构造器会创建一个默认大小(通常是16)的字符数组。在使用中,如果超出这个大小,就会重新分配内存,创建一个更大的数组,并将原先的数组复制过来,再丢弃旧的数组。在大多数情况下,你可以在创建StringBuffer的时候指定大小,这样就避免了在容量不够的时候自动增长,以提高性能。

例子:

public class RSBC {

     voidmethod () {

        StringBuffer buffer = new StringBuffer(); // violation

        buffer.append ("hello");

     }

}

更正:

确定StringBuffer提供的大小。

public class RSBC {

     voidmethod () {

        StringBuffer buffer = new StringBuffer(MAX);

        buffer.append ("hello");

     }

     privatefinal int MAX = 100;

}

 

以上对一些经常出现的对效率有所影响Java的优化建议进行了详细的描述,其实在实践过程中仍然有许多大家需要注意和优化的地方,譬如:使用System.arraycopy()代替通过来循环复制数组;为Vectors和Hashtables定义初始大小;查找单个字符的话,用charAt()代替startsWith();对于boolean值,避免不必要的等式判断等等,这些都能或多或少的提高效率。对于未涉及的Java优化的最佳实践,在实际项目组编写Java的过程中,通过跟踪程序,精确到平台程序的具体方法,从执行效率和算法逻辑两方面进行优化。以后通过项目的不断积累,会有越来越多好的方法和实践展现在大家面前。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值