基本知識

1.1 性能是什么

在性能調(diào)優(yōu)之前,我們首先來了解一下性能是什么?關(guān)于性能,我想每個學(xué)習(xí)過Java的人都能列出幾點,甚至可以夸夸其談。在《Java TM Platform Performance》(注:我有此書的電子版,有需要者可以和我聯(lián)系)一書中,定義了如下五個方面來作為評判性能的標(biāo)準(zhǔn):

1)      運算的性能——哪一個算法的執(zhí)行性能最好?

2)      內(nèi)存的分配——程序運行時需要耗費多少內(nèi)存?

3)      啟動的時間——程序啟動需要多長時間?這在Web項目中的影響不大,但要注意部分程序需要部署或運行在客戶端時的情形(比如applet程序)。

4)      程序的可伸縮性——在壓力負(fù)載的情況下,程序的性能如何?

5)      性能的感知——用戶在什么情況下會覺得程序的性能不好?

以上五個方面,在具體的使用場景可以有選擇的去評判。至于這五方面的性能調(diào)優(yōu),在后續(xù)的章節(jié)中將會陸續(xù)的給以相應(yīng)的性能調(diào)優(yōu)策略。

1.2 調(diào)優(yōu)的規(guī)則

我們只需要關(guān)心對我們程序有影響,可以察覺到的性能問題,而不是每一個類中的每一個方法我們都需要想方設(shè)法的提高性能。如果程序的性能沒有達(dá)到我們所期望的要求,我們才需要考慮如何優(yōu)化性能。同樣的,晦澀的代碼雖然提高了程序的性能,但同時可能帶給我們的是維護(hù)的噩夢。我們需要折中的考慮以上兩種情況,使得程序的代碼是優(yōu)美的,并且運行的足夠快,達(dá)到客戶所期望的性能要求。

優(yōu)化代碼甚至?xí)?dǎo)致不良的結(jié)果,Donald Knuth(一位比較牛比較有影響的人物,具體是誰,我也忘了,誰知道,可以告訴我一下,謝謝!)曾說過,“Premature optimization is the root of all evil”。在開始性能調(diào)優(yōu)前,需要先指出不優(yōu)化代碼的一些理由。

1)      如果優(yōu)化的代碼已經(jīng)正常工作,優(yōu)化后可能會引入新的bug

2)      優(yōu)化代碼趨向于使代碼更難理解和維護(hù);

3)      在一個平臺上優(yōu)化的代碼,在另一個平臺上可能更糟;

4)      花費很多時間在代碼的優(yōu)化上,提高了很少的性能,卻導(dǎo)致了晦澀的代碼。

確實,在優(yōu)化前,我們必須認(rèn)真的考慮是否值得去優(yōu)化。

1.3 調(diào)優(yōu)的步驟

一般我們提高應(yīng)用程序的性能劃分為以下幾個步驟:

1)      明確應(yīng)用程序的性能指標(biāo),怎樣才符合期望的性能需求;

2)      在目標(biāo)平臺進(jìn)行測試;

3)      如果性能已經(jīng)達(dá)到性能指標(biāo),Stop

4)      查找性能瓶頸;

5)      修改性能瓶頸;

6)      返回到第2步。

JDK調(diào)優(yōu)

2.1 選擇合適的JDK版本

不同版本的JDK,甚至不同廠家的JDK可能都存在著很大的差異,對于性能優(yōu)化的程度不同。一般來說,盡可能選擇最新發(fā)布的穩(wěn)定的JDK版本。最新的穩(wěn)定的JDK版本相對以前的JDK版本都會做一些bug的修改和性能的優(yōu)化工作。

2.2 垃圾收集Java堆的優(yōu)化

垃圾收集就是自動釋放不再被程序所使用的對象的過程。當(dāng)一個對象不再被程序所引用時,它所引用的堆空間可以被回收,以便被后續(xù)的新對象所使用。垃圾收集器必須能夠斷定哪些對象是不再被引用的,并且能夠把它們所占據(jù)的堆空間釋放出來。如果對象不再被使用,但還有被程序所引用,這時是不能被垃圾收集器所回收的,此時就是所謂的“內(nèi)存泄漏”。監(jiān)控應(yīng)用程序是否發(fā)生了內(nèi)存泄漏,有一個非常優(yōu)秀的監(jiān)控工具推薦給大家——Quest公司的JProbe工具,使用它來觀察程序運行期的內(nèi)存變化,并可產(chǎn)生內(nèi)存快照,從而分析并定位內(nèi)存泄漏的確切位置,可以精確定位到源碼內(nèi)。這個工具的使用我在后續(xù)的章節(jié)中還會做具體介紹。

Java堆是指在程序運行時分配給對象生存的空間。通過-mx/-Xmx-ms/-Xms來設(shè)置起始堆的大小和最大堆的大小。根據(jù)自己JDK的版本和廠家決定使用-mx-ms-Xmx-XmsJava堆大小決定了垃圾回收的頻度和速度,Java堆越大,垃圾回收的頻度越低,速度越慢。同理,Java堆越小,垃圾回收的頻度越高,速度越快。要想設(shè)置比較理想的參數(shù),還是需要了解一些基礎(chǔ)知識的。

Java堆的最大值不能太大,這樣會造成系統(tǒng)內(nèi)存被頻繁的交換和分頁。所以最大內(nèi)存必須低于物理內(nèi)存減去其他應(yīng)用程序和進(jìn)程需要的內(nèi)存。而且堆設(shè)置的太大,造成垃圾回收的時間過長,這樣將得不償失,極大的影響程序的性能。以下是一些經(jīng)常使用的參數(shù)設(shè)置:

1)      設(shè)置-Xms等于-XmX的值;

2)      估計內(nèi)存中存活對象所占的空間的大小,設(shè)置-Xms等于此值,-Xmx四倍于此值;

3)      設(shè)置-Xms等于-Xmx1/2大小;

4)      設(shè)置-Xms介于-Xmx1/101/4之間;

5)      使用默認(rèn)的設(shè)置。

大家需要根據(jù)自己的運行程序的具體使用場景,來確定最適合自己的參數(shù)設(shè)置。

除了-Xms-Xmx兩個最重要的參數(shù)外,還有很多可能會用到的參數(shù),這些參數(shù)通常強烈的依賴于垃圾收集的算法,所以可能因為JDK的版本和廠家而有所不同。但這些參數(shù)一般在Web開發(fā)中用的比較少,我就不做詳細(xì)介紹了。在實際的應(yīng)用中注意設(shè)置-Xms-Xmx使其盡可能的優(yōu)化應(yīng)用程序就行了。對于性能要求很高的程序,就需要自己再多研究研究Java虛擬機和垃圾收集算法的機制了。可以看看曹曉鋼翻譯的《深入Java虛擬機》一書。