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

  • 相关阅读:
    智慧养老民政监管平台建设方案
    CF600E Lomsat gelral dsu on tree
    dsu on tree详解
    【Spring 从0开始】Spring5 新功能,整合日志框架 Log4j2
    【Spring 从0开始】JdbcTemplate 数据库事务管理
    【Spring 从0开始】JdbcTemplate 数据库事务参数
    【Spring 从0开始】JdbcTemplate 数据库事务管理
    【Spring 从0开始】JdbcTemplate 操作数据库
    【Spring 从0开始】AOP 操作
    【Spring 从0开始】AOP 操作中的相关术语、环境准备
  • 原文地址:https://www.cnblogs.com/snake107/p/11939034.html
Copyright © 2011-2022 走看看