你的位置:首页 > Java教程

[Java教程]java程序性能优化


 

Java程序调优入门级教程,看似很厚,其实书很薄,国内高手写的,当然有一些内容有抄袭的嫌疑,适合java开发经验不太足的,一般的高手,此类技能应该必须具备了。我看的时候花了两周,总结起来如下,纯干货(不包括JVM部分,因为个人觉得JVM学习看《深入理解Java虚拟机》):

 

1.常见的系统瓶颈因素:磁盘I/O,网络操作,CPU,java异常捕获及处理,数据库读写,锁竞争,GC

2.加速比Speedup<=1/(F+(1-F)/N),F为串行计算的比例,N为CPU逻辑数

3.调优层次:设计调优--->代码调优--->JVM参数调优--->数据库调优--->OS调优,当实际应用出现瓶颈需要调优时,一般的步骤是:代码--->JVM参数--->数据库--->OS--->应用架构(设计)

4.设计调优的常见招数:

        1:多用设计模式(单例,享元,观察者,访问者,装饰者,代理等)

        2:Buffer/Cache,缓冲/缓存思想

        3:多线程并行代替串行(要求一般串行间没有因果或前后关系)

        4:应用负载均衡(JVM级,需要处理好共享数据的一致性问题)

        5:时间/空间的转换

        6:使用ValueObject减少计算或请求次数

5.代码优化的几个简单切入点:

        0):不要在循环体里定义引用并创建对象,将定义引用放到外面

        Objectobj = null;

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

   (Object) obj = new Object();

   System.out.println("obj= "+ obj);

}

1):避免重复初始化对象

public class A {

    private Hashtable table = new Hashtable();

   public A() {        

       table = new Hashtable(); // 将Hashtable对象table初始化了两次

   }

}

        1):字符串优化的几个地方

                a:substring()方法速度很快,但是容易引起内存溢出问题,因为会保留对原串的引用,所以建议用newString(***.substring(参数))的模式

               b:对字符串截取可用split,但速度快的可以考虑StringTokenizer(sun不建议使用),最好用indexOf+substring自己实现截取

                c:对字符串的开头/结尾子串判断一般用startsWith/endsWith,由于他们和split一样是基于正则的,所有最好用charAt来自己实现更快

                d:对字符串的相加,StringBuild最优,StringBuffer次之(线程安全),String.concat再次之,+/+=最次(不考虑静态常量编译器的优化)

        2):ArrayList(Vector)与LinkedList比较

                a:ArrayList基于数组(一块连续的内存空间),LinkedList基于双向链表,因此ArrayList的主要性能耗在扩展空间时数据复制,而LinkedList耗在数据遍历(尤其是找中间的数据)

                b:头尾的新增/删除对LinkedList没有压力,但是每次随机访问需要从头查找,会要亲命

                c:在尾部添加对ArrayList没有压力,但从头部删除都会数组复制,会要亲命。

                d:对LinkedList的遍历,要么用for-Each,要么Iterator,不要用普通的ini i=0;i<size;i++,因为get(i)是随机查找,每次都从头或尾遍历,会要亲命

                e:一般实现了RandomAccess接口的类如ArrayList,Vector才可以在遍历时放心使用get,而且遍历时要尽量减少重复的获取size或元素,用临时变量缓存之

        3):HashMap如果key是一个个重写了hashCode方法的对象,如果此hashCode方法放回相同的int值,则每次放入不同对象会到同一个桶下,遍历查找很慢

        4):HashMap底层用了数组实现,使用大于等于initialCapacity并且是2的指数次幂作为数组大小,threshold为当前数组总容量与负载因子的乘积,即阀值,当实际容量超阀值        时会进行数组扩展复制,因此适当的initialCapacity有利于提高性能。

        5):LinkedHashMap基于元素进入顺序或被访问先后顺序(被访问的元素放到最后),而TreeMap基于传入的Comparetor参数或实现了Comparable的key对象进行排序,适用范围        不一样。HashSet,LinkedHashSet,TreeSet是对应Map的简单封装。

        6):使用NIO(ByteBuffer+Channel)可以提升I/O读写速率,注意duplicate(),asReadOnlyBuffer(),slice()都是共享原始缓存数据的,一般用文件内存映射MappedByteBuffer,结构       化散射接口ScatteringByteChannel,结构化聚集接口GatheringByteChannel将多个ByteBuffer组成一个数组一次写入,用DirectBuffer代替ByteBuffer进行多次读写都可以提高效   率,但DirectBuffer是直接开辟OS内存,如果频繁新建回收,性能反而不如ByteBuffer虚拟机内的相同操作.

        7):4个引用级别:强,软,弱,虚,其作用分别不同,一般软和弱引用可以作为缓存的一种方案避免内存溢出,而虚更多用于跟踪对象回收时机,弱引用WeakHashMap可以        作为简单的缓存对象。

        8):不在循环代码中使用try-catch,尽量放到循环体外,多用局部变量或临时变量存储计算值以避免重复计算,可以考虑在一次循环中做多个操作减少循环次数(i+1,i+2,i+3...),多        用"阻断性"逻辑运算如&&来代替位运算如&(当然一般也不建议利用条件判断来做一些操作,因此后面的判断默认应该可以不去运行的)

        9):多用一维数组代替二维数组;使用Buffer进行I/O;对构造成本大的函数,用clone代替new(注意该类实现Cloneable,并且默认是浅复制,深复制需要自己重写Object的clone        方法)

        10):技巧:用位运算代替2次幂的乘除;多用static静态方法而不是实例方法;多用native方法如arrayCopy等;

 

 

 

 

 

 

 

 

 

 




来自为知笔记(Wiz)