局部决定总体。
一个应用的总体性能取决于每一个组件的性能。
以下是一些帮助你提高应用性能的Java编程技巧:
编程技巧 |
原因及策略 |
避免反复创建对象 |
为什么:
-
更少的对象会须要更少的垃圾回收
-
使用的空间越少,应用的性能越好
怎么做:
-
反复利用一个对象,而不是在每次须要的时候都去创建一个功能一样的对象
-
(这样做)
-
String s = “No longer silly”;
-
(不要这样)
-
String s = new String(“silly”);
-
不可变类中既提供构造函数,又提供了静态工厂方法的。优先考虑使用静态工厂方法。
-
复用那些一旦初始化(使用静态初始化)就不会改变的对象
|
避免循环引用 |
为什么:
-
一组相互引用的对象,假设他们没有被其它对象直接引用的话。它们会变得不可达,这样会导致它们一直都保留在内存里。
怎么做:
-
你能够使用强引用来表示“父到子“的引用关系。使用弱引用来表示“子到父”的引用关系。
|
使用==操作符来替代equals(Object)方法 |
为什么:
-
==操作符的性能更好
-
比如,对于字符串比較,equals()方法会去比較字符串对象里的字符。==操作符会比較两个对象的引用,来比較它们是否指向同一个实例。
怎么做:
-
当且仅当a == b 的时候才会有a.equals(b)
-
比如,对于反复调用的地方,使用静态工厂方法来返回同样的对象。
|
清除没用的对象的引用 |
为什么:
-
没用的对象引用会导致很多其它的垃圾回收动作,从而减少性能
-
没用的对象引用会导致很多其它的内存占用。从而减少性能
怎么做:
-
假设一个引用时废弃的话,把它设置为null
-
(这样做)
-
1
2
3
4
5
6
7
8
|
public Object
pop() {
if (size
==
0 )
throw new
EmptyStackException();
Object
result = elements[--size];
elements[size]
=
null ; //
清除无用对象的引用
return result;
}
|
-
(不要这样)
-
1
2
3
4
5
|
public Object
pop(){
if (size
==
0 )
throw new
EmptyStackException();
return elements[--size];
}
|
|
避免使用finalizer |
为什么:
-
垃圾回收器须要单独记录等待终结的对象
-
调用finalize方法也有一定的开销
-
Finalizer是不安全的。由于它有可能会复活一个对象,这样会干扰垃圾回收。
|
避免使用引用对象 |
为什么:
-
和finalizer一样,垃圾回收器须要特别处理软引用、弱引用以及幽灵引用。
-
虽然引用对象在某些方面非常有作用,比如,简化cache的实现。可是大量引用对象的存在会使得垃圾回收执行缓慢。
-
记录一个引用对象的开销远远超过一个普通对象(强引用)的开销
|
避免使用对象池 |
为什么:
-
对象池不仅会使得很多其它的数据对象保持活动,同一时候会使得对象的存活时间延长
-
值得注意的是,大量存活的数据对象的处理是GC的瓶颈,GC被优化成适合于处理很多寿命较短的对象
-
而且,创建新的对象而不是保持旧的对象存活,会对缓存的局部性故意
-
只是,在一个包括大量大对象的环境下,比如大的数组,性能也许会由于使用对象池而有所提升。
|
选择好的算法和数据结构 |
为什么:
-
考虑一下通过链表来实现队列的场景
-
即使你的程序不须要遍历整个链表,可是垃圾回收器还是须要这样做的。
-
假设元素的封装者没有把元素没有把元素放在内存中邻近的位置,这样会破坏缓存局部性。因而会导致程序长时间的暂停,尤其是对象的指针分散在一个非常大的堆区时,垃圾回收器会在标记阶段追随指针的时候频繁遭遇缓存失效。
|
避免使用System.gc |
为什么:
-
Java语言规范里没有保证调用System.gc会做什么事情。假设它规定了的话。也许会超出你的期望,也也许每次调用都做不同的事情。
|
避免使用太多的线程 |
为什么:
-
进程上下文切换的次数会随着要调度的进程的数目对应地增长。这样会对性能有隐性的影响。
-
比如。Intel A-64处理器上的本地线程上下文的大小大概是几千KB
|
避免使用竞争锁 |
为什么:
-
竞争锁一般都是程序的瓶颈。由于它的出现意味着多个线程想訪问同一个资源或者运行同一段代码。
|
避免不须要的异常 |
为什么:
-
异常处理会占用一定的事件,而且会打断程序的正常运行流程。
-
作者以前遇到这样一场景,在客户的应用里,一个正常的运行流程每秒会抛出成千上万的NullPointerException。这个错误被纠正后,应用的性能里面有了一个数量级的提升。
|
避免使用大对象 |
为什么:
-
大对象有时候须要直接在堆而不是在线程本地存储区(thread local areas, TLA)进行内存分配。
-
大对象直接在堆上分配是有坏处的。由于它会更快地产生内存碎片。
-
在虚拟机(比如JRockit)上分配大对象会减少性能。由于分配内存的时候会使用堆的全局锁。
-
过度使用大对象会造成频繁的全栈压缩。这样做是具有破坏性的,并且这样会导致导致全部的线程暂停非常长一段时间。
|
參考书籍
-
Oracle
JRockit
-
Effective
Java by Joshua Bloch
英文原文:xmlandmore,编译:ImportNew - 朱伟杰
译文链接:http://www.importnew.com/1531.html