zoukankan      html  css  js  c++  java
  • Java程序性能优化

    Java程序性能优化

    最近在做code review时,也对这段时间写的代码做了一次全身“体检”,对于结果,受益匪浅,至少知道目前还有需要提升的空间,所以整理了这次“体检结果”,并结合自身体会以及程序优化的重要性,增加了一些Java程序有必要需要知道的优化细节,如果有不对之处,还请各位指正,当然了,程序优化道路千千万,希望各位有什么好的优化建议,还望请教。

    程序优化的目标

    首先,程序的优化,除了要消除错误之外,还有一些不易发觉的细节,这些细节可能不影响你现有代码的运行,从性能上来说,好像也看不出什么差别,但是长此以往,不仅对整个系统会造成不良影响,也会对后期维护带来更大的成本,更重要的是,会对我们程序猿的编程思维、编程习惯等造成后患。所以,我们在写代码的时候,从源头上开始注意各个细节,权衡并使用最佳的选择,将会很大程度上提高程序的性能,提高效率。那么既然时程序性能优化,当然我们的优化目标就是

    • 从时间上提高运行效率
    • 从空间上减小性能损耗

    代码优化细节

    部分参考自《Effective Java》以及《阿里巴巴Java开发手册》,关于命名规范等在此不做讨论,另外,也可以在IDE中下载安装Alibaba Java Coding Guidelines插件,时刻提醒自己。

    1、静态工厂方法代替构造器

    我们通常在程序中创建对象,一般都是使用new关键字,通过构造器来创建对象,但是也有的是通过静态方法来创建对象,比如下面的将boolean类型转换为Boolean类型。

    public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
    }
    

    这样做的好处就是,不必每次调用的时候都去创建对象,减少创建对象开辟的空间,在一定程度上优化了内存消耗。这也是《Effective Java》一书中推荐的写法。

    2、尽量做到重用对象

    比如String对象,在做字符串拼接的时候,很多人喜欢用+号拼接,这样做其实是很消耗内存的,因为每次拼接的时候,并不是在原有基础上进行拼接,而是会创建一个新的对象,在进行拼接,而拼接后的又是一个新的对象,如果出现大量字符串拼接的话,这无疑将会给程序的性能带来很大的影响。所以在做字符串拼接的时候,我们应尽量使用StringBuilder或者StringBuffer。

    3、及时释放资源

    很多时候,我们在进行数据连接操作,或者是IO操作时,应在使用结束后及时关闭以释放资源。

    4、减少变量的重复计算、方法的重复调用

    在调用方法的时候,如果在循环中调用同一个方法,即便该方法消耗很小,那也应该减小这种不必要的消耗。比如最常见的写法:

    List<String> list = new ArrayList<>();
    for (int i = 0; i < list.size(); i++) {
        //todo
    }
    

    在上述代码中,循环调用size()方法,如果size()结果很大,那肯定会造成一些不必要的损耗。那么建议换成如下写法:

    List<String> list = new ArrayList<>();
    int size = list.size();
    for (int i = 0; i < size; i++) {
        //todo
    }
    
    5、必要时采用懒加载策略

    看下面一段代码:

    List<String> list = new ArrayList<>();
        String str = "world";
        for (String item : list) {
            if ("hello".equals(item)) {
                list.add(str);
            }
        }
    

    咋一看,好像并没有什么问题。的确是没有什么问题,但是细想一下,如果list集合中并没有“hello”这个字符串,那我们岂不是在内存中白白占用了一块地创建“world”?所以是否应换成如下写法呢:

    List<String> list = new ArrayList<>();
        for (String item : list) {
            if ("hello".equals(item)) {
                String str = "world";
                list.add(str);
            }
        }
    
    6、底层数组实现的集合框架,尽量指定存储容量

    在操作如ArrayList、StringBuilder、HashMap之类的集合框架时,如果能知道大概要存储多少数据,那应尽量在初始化容器的时候,指定容器的存储容量。
    就拿StringBuiler来说,当它达到最大容量的时候,便会进行扩容,将自身容量扩大到原来的2倍加2,在扩容的时候,StringBuilder会创建一个新的字符串数组,然后将旧的字符数组拷贝到新的字符数组中,这是一个很耗费性能的操作。所以,如果我们在预先知道存储容量的时候,在创建StringBuilder的时候,应指定容量大小。

    7、尽量避免在循环内创建对象
    List<String> list = new ArrayList<>();
    int size = list.size();
    for (int i = 0; i < size; i++) {
        Object o = new Object();
        //todo
    }
    

    很明显,这样做,会创建size份对象,占用大量内存空间,所以必要的话,可改成:

    List<String> list = new ArrayList<>();
    int size = list.size();
    Object o = null;
    for (int i = 0; i < size; i++) {
        o = new Object();
        //todo
    }
    
    8、性能和线程安全性之间的选择

    在Java中提供了很多集合相关的类给我们使用,并且这些集合类各有优缺点,在此需要说明的是,在使用集合框架类时,如果是单线程或者对线程安全性没有太大要求,那么尽量使用ArrayList、HashMap、StringBuilder等,而在对线程安全性有高要求的话,应尽量使用Vector、HashTable、StringBuffer等。

    在JDK1.8以后,提供了更多在性能和线程安全方面的选择,后续在Java集合框架系列中会说明。

    9、减少静态变量的使用

    当某个对象被定义为static的变量所引用,那么gc通常是不会回收这个对象所占有的堆内存的,如:

    public class Test {
        private static Object o = new Object();  
    }
    

    此时静态变量o的生命周期与Test类相同,如果Test类不被卸载,那么引用o指向的Object对象会常驻内存,直到程序终止。

    10、集合中选择最佳的方式进行遍历操作

    在集合框架中,实现了RandomAccess接口的类,支持快速随机访问,在遍历时,应使用普通for循环的方式,而没有实现RandomAccess接口的类,应该使用iterator的方式进行遍历。可参考另一篇文章:ArrayList为什么要实现RandomAccess接口?

    本文转自https://www.jianshu.com/p/93472e9bce81

  • 相关阅读:
    Css时间轴布局_Css时间轴布局案例整理
    Java Efficient data transfer through zero copy
    Interviewing at Amazon — Leadership Principles
    【转】golang 锁使用---里面的读写锁解析
    【转】MySQL GRANT:用户授权
    【转】mysql处理高并发,防止库存超卖
    [转]Character encoding for commit messages
    【转】git tag的用法
    【转】断网后VirtualBox连接不上问题解决
    【转】sql基础left join, inner join, full join , right join 的理解
  • 原文地址:https://www.cnblogs.com/snake107/p/11939034.html
Copyright © 2011-2022 走看看