• 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
    {
        void method(Vector vector)
        {
            for (int i = 0; i < vector.size(); i++)
                ; // 执行操作
        }
    }

    更正:

    class CEL_fixed
    {
        void method (Vector vector) {
            int size = vector.size();
            for (int i = 0; i < size; i++)
                ; // 执行操作
         }
    }

    7、使用finally释放资源

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

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

    class MAF
    {
        public void setSize(int size)
        {
            _size = size;
        }
        private int _size;
    }

    更正:

    class MAF
    {
        final public void setSize(int size)
        {
            _size = size;
        }
        private int _size;
    }

    9、避免不需要的instanceof操作

    如果左边的对象的静态类型等于右边的,instanceof表达式返回永远为true。所以也根本不需要做判断。

    public class UISO
    {
        public UISO(){}
    }
    
    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");
        }
    }

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

    用new关键词创建类的实例时,构造函数链中的所有构造函数都会被自动调用。但如果一个对象实现了Cloneable接口,我们可以调用它的clone()方法。clone()方法不会调用任何类构造函数。
    在使用设计模式(Design Pattern)的场合,如果用Factory模式创建对象,则改用clone()方法创建新的对象实例非常简单。例如,下面是Factory模式的一个典型实现:

        public static CreditgetNewCredit()
        {
            return new Credit();
        }

    改进后:

        private static Credit BaseCredit = new Credit();
    
        public static CreditgetNewCredit()
        {
            return (Credit) BaseCredit.clone();
        }

    11、避免不需要的强转操作

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

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

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

    public class SYN
    {
        public synchronized void method(Object o)
        {
        }
    
        private void test()
        {
            for (int i = 0; i < vector.size(); i++)
            {
                method(vector.elementAt(i)); // violation
            }
        }
    
        private Vector vector = new Vector(5, 5);
    }

    更正:

    public class SYN
    {
        public void method(Object o)
        {
        }
    
        private void test () {
            //在一个同步块中执行非同步方法
            synchronized (vector)
            {
                for (int i = 0; i < vector.size(); i++) {
                    method (vector.elementAt(i));  
                }
            }
         }
    
        private Vector vector = new Vector(5, 5);
    }

    13、将try/catch块移出循环

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

    import java.io.FileInputStream;
    
    public class TRY
    {
        void method(FileInputStream fis)
        {
            for (int i = 0; i < size; i++)
            {
                try
                { // violation
                    _sum += fis.read();
                }
                catch (Exception e)
                {
                }
            }
        }
        private int _sum;
    }

    更正:

        void method(FileInputStream fis)
        {
            try
            { // violation
                for (int i = 0; i < size; i++)
                {
                        _sum += fis.read();
                }
            }
            catch (Exception e)
            {
            }
        }

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

    常量字符串并不需要动态改变长度。把StringBuffer换成String,如果确定这个String不会再变的话,这将会减少运行开销提高性能。

    public class USC
    {
        String method()
        {
            StringBuffer s = new StringBuffer("Hello");
            String t = s + "World!";
            return t;
        }
    }

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

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

    import java.util.Vector;
    
    public class LOOP
    {
        void method(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
    {
        void method(Vector v)
        {
            Object o;
            for (int i = 0; i < v.size(); i++)
            {
                o = v.elementAt(i);
            }
        }
    }

    16、确定StringBuffer的容量

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

    public class RSBC
    {
        void method()
        {
            StringBuffer buffer = new StringBuffer(); // violation
            buffer.append("hello");
        }
    }

    更正:

    public class RSBC
    {
        void method()
        {
            StringBuffer buffer = new StringBuffer(MAX);
            buffer.append("hello");
        }
    
        private final int MAX = 100;
    }

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

  • 相关阅读:
    程序启动冲出UAC解决Win UAC问题的编程经验
    代码服务器端Tornado实现聊天室功能(websocket)
    选中菜单android中的显示跳转和隐式跳转
    提交优化Oracle Tuning Log File Sync 等待事件的几种策略
    对象类Effective Java:Ch3_Methods:Item11_谨慎重写clone()
    视频教程视频Java+PHP+.NET海量教程来了 500G教程
    应用性能Intel MIC适合什么样的应用程序
    资源页面dwr的ajax实现
    登录用户Eclipse中SVN访问用户的变更办法
    内存释放[置顶] Cocos2dx 内存管理心得
  • 原文地址:https://www.cnblogs.com/hnhcc39/p/2627872.html
Copyright © 2020-2023  润新知