月写意&随月

导航

性能优化

最近在做框架性能优化的时候,发现了一些之前不是很在意却对性能很有影响的内容,同时和以前知道的东西一起做了整理

1、关于比较

最老生常谈的就是==,equals,一个比较绝对值,一个比较引用地址,一般字符串的比较我们直接用equals

但如果我们的数据类型是Integer的时候呢,比较2个Integer是否相等,你是否会写成==?。如果你写了,那么就错了,比较整型的数值你需要这样来写:a.intValue() == b.intValue()

 

2、ArrayList删除一个元素
   作为集合类中最常用的ArrayList,删除一个元素一般有.remove(obj)、和.remove(index)2种方式,通常我们不会太在意调用哪个,写到哪个是哪个。尤其是我们要实现从list中找到满足条件的多个元素,然后删除掉的时候,为了避免使用i--,出现index混乱的问题,我们会使用一个临时的list来存储好满足条件的obj,最后调用list.removeAll(list)来进行。殊不知,这样的写法大大消耗了性能。最近在做性能优化,跟踪cpu消耗的时候,发现了这个removeAll的问题。它内部实现原理会去重新定位找到这个obj对象,然后调用remove删除,而定位obj的过程需要用到equals来进行2个对象的比较,一般我们定义的对象并不会去实现equals方法,所以这个性能消耗就有一点了

 

3、序列化 Serializable
   在集群开发的时候,我们经常会涉及到序列化对象存储到redis中,比如我们的上传控件,为了支持集群上传,我们action里面定义的上传model会进行序列化保存,来保证不同服务器接收到客户端传来的文件片可以使用同一套业务逻辑来进行处理,所以涉及到的类上面我们都需要添加这个关键字。然后会出现一个黄色惊叹号,点出来后建议大家生成一串随机数值,而不是1L,这样保证在编译反序列化时不会出现不同版本代码导致反序列化失败的问题。另外某些同事启动tomcat的时候,经常会发现有警告,某某类不能序列化,但是不影响使用,实际上就是这些类上面没有加上序列化接口导致。


4、transient关键字
很多人不认识或者没有使用过这个关键字,它不同于public、private、protected这么常见。但是在集群模式的web开发中,这个东西就会派上用场。这个关键字修饰的成员变量,将不会进行序列化。某些变量我们是不能序列化的,比如Log4j的日志对象,或者我们的Service、Dao对象,都不能进行序列化,或者是本身序列化就不支持会报错,或者反序列化出来使用的时候会出现问题,不一而足。

 

5、初始化大小
StringBuffer 、ArrayList

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

例子:        
public class rsbc {
    void method () {
        stringbuffer buffer = new stringbuffer(); // violation
        buffer.append ("hello");
    }
}    
更正:        
为stringbuffer提供寝大小。        
public class rsbc {
      private final int max = 100;
    void method () {
        stringbuffer buffer = new stringbuffer(max);
        buffer.append ("hello");
    } 
}
当你要创建一个比较大的hashMap时,充分利用另一个构造函数

public HashMap(int initialCapacity, float loadFactor)避免HashMap多次进行了hash重构,扩容是一件很耗费性能的事,在默认initialCapacity只有16,而 loadFactor是 0.75,需要多大的容量,你最好能准确的估计你所需要的最佳大小,同样的Hashtable,Vectors也是一样的道理。

 

6、单一对象转list
        FrameOu ou = new FrameOu();
        ou.setOuname("test");

        //一般我们会这样来写:       

        List<FrameOu> list = new ArrayList<FrameOu>();
        list.add(ou);
        BeanXmlUtil.beanListToXml(list);

        //其实我们一句话就搞定了:       

        BeanXmlUtil.beanListToXml(Arrays.asList(ou));

 

7、将try/catch块移出循环

把try/catch块放入循环体内,会极大的影响性能,如果编译jit被关闭或者你所使用的是一个不带jit的jvm,性能会将下降21%之多!
        
例子:        
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;
}

        
更正:        
将try/catch块移出循环        
 
 void method (fileinputstream fis) {
        try {
            for (int i = 0; i < size; i++) {
                _sum += fis.read();
            }
        } catch (exception e) {}
    }

 

8、尽量使用final修饰符
带有final修饰符的类是不可派生的。在JAVA核心API中,有许多应用final的例子,例如 java.lang.String。为String类指定final防止了使用者覆盖length()方法。另外,如果一个类是final的,则该类所有方法都是final的。java编译器会寻找机会内联(inline)所有的final方法(这和具体的编译器实现有关)。此举能够使性能平均提高 50%。

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

例子:
class maf {
    public void setsize (int size) {
         _size = size;
    }
    private int _size;
}


更正:
class daf_fixed {
    final public void setsize (int size) {
         _size = size;
    }
    private int _size;
}

我们实体的get、set,action里面的基本都可以考虑加上final来提升效率

9、HaspMap的遍历

我们常常会这样来写:

Set<String> keys = map.keySet();
    for (String item : keys) {
        String value =  map.get(item) ;
   }
}

实际上更高效的应该是这样:
Map<String, String[]> paraMap = new HashMap<String, String[]>();
for( Entry<String, String[]> entry : paraMap.entrySet() )
{
    String appFieldDefId = entry.getKey();
    String[] values = entry.getValue();
}

 

10、在java+Oracle的应用系统开发中,java中内嵌的SQL语言应尽量使用大写形式,以减少Oracle解析器的解析负担。

 

11、StringBuffer,StringBuilder的抉择
区别在于:java.lang.StringBuffer 线程安全的可变字符序列。一个类似于String的字符串缓冲区,但不能修改。StringBuilder与该类相比,通常应该优先使用 StringBuilder类,因为她支持所有相同的操作,但由于她不执行同步,所以速度更快。为了获得更好的性能,在构造StringBuffer或 StringBuilder时应尽量指定她的容量。当然如果不超过16个字符时就不用了。
相同情况下,使用StringBuilder比使用 StringBuffer仅能获得10%~15%的性能提升,但却要冒多线程不安全的风险。综合考虑还是建议使用StringBuffer

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

在不做编译优化的情况下,在循环中,循环条件会被反复计算,如果不使用复杂表达式,而使循环条件值不变的话,程序将会运行的更快。
比如
for(int i=0;i<list.size();i++)

应修改为
for(int i=0,len=list.size();i<len;i++)

 

13、采用在需要的时候才开始创建的策略。
例如:
String str="abc";
if(i==1){ list.add(str);}

应修改为:
if(i==1){String str="abc"; list.add(str);}

 

14、io读取
之前解决过一个下载大文件很慢的问题,查看发现每次读取一个子节点,性能都消耗在循环上面了

            int ch = 0;
            while ((ch = fis.read()) != -1) {
                baos.write(ch);
            }

改成缓冲区读取后快了很多:

            byte[] buffer = new byte[1024 * 4];
            baos = new ByteArrayOutputStream();
            int ch = 0;
            while ((ch = fis.read(buffer)) != -1) {
                baos.write(buffer, 0, ch);
            }

15、缓存中使用的同步集合

   当我们做数据本地缓存需要使用到集合的时候,推荐使用自带同步功能的ConcurrentHashMap和CopyOnWriteArrayList,虽然性能有所下降,但对于改动不是太频繁,而且并发安全来说,是最佳的选择。

posted on 2016-09-27 11:39  月写意&随月  阅读(218)  评论(0编辑  收藏  举报