唐 科
(電子科技大學(xué)成都學(xué)院 計(jì)算機(jī)系,四川 成都 611731)
在各類Java應(yīng)用系統(tǒng)中,為了給大量并發(fā)用戶提供7×24小時(shí)持續(xù)不間斷響應(yīng)流暢的訪問體驗(yàn),系統(tǒng)設(shè)計(jì)必須精良。同時(shí),在后續(xù)開發(fā)與維護(hù)過程中也必須考慮擴(kuò)展性、可靠性,其核心就是滿足系統(tǒng)的性能需求,給用戶提供優(yōu)良的體驗(yàn)。
系統(tǒng)性能由應(yīng)用程序、系統(tǒng)環(huán)境、硬件配置等諸多因素決定,一種特定的配置無(wú)法滿足所有特性各異的上層應(yīng)用性能需求[1]。在這些因素中,確定的硬件和系統(tǒng)架構(gòu)決定了底層的訪問速度與吞吐量。良好的頂層軟件設(shè)計(jì)(應(yīng)用程序設(shè)計(jì))卻能充分利用硬件和系統(tǒng)架構(gòu),最大限度地發(fā)揮系統(tǒng)資源的利用效率,形成性能優(yōu)良的應(yīng)用系統(tǒng)。所以,系統(tǒng)性能的優(yōu)化是由上述諸多因素相互作用決定的。
Java應(yīng)用系統(tǒng)性能深受開發(fā)者及用戶重視,它是應(yīng)用系統(tǒng)的基礎(chǔ),一旦受損后果都是災(zāi)難性的。所以,確保應(yīng)用系統(tǒng)性能始終處于優(yōu)良狀態(tài),其重要性毋庸置疑。基于此,研究人員提出了很多技術(shù)方法來(lái)優(yōu)化系統(tǒng)性能,但這些方法各自為政,不能形成系統(tǒng)優(yōu)化措施。針對(duì)此問題,本文提出自頂向下的Java應(yīng)用系統(tǒng)性能優(yōu)化方法,通過形成體系化的調(diào)優(yōu)策略達(dá)到優(yōu)化系統(tǒng)性能目的。
在異構(gòu)系統(tǒng)編程環(huán)境應(yīng)用中,通過對(duì)編程容易度和編程應(yīng)用性能的綜合比較研究,得出表1與表2所示的結(jié)論[14]。Java具備良好的共享內(nèi)存自動(dòng)分配機(jī)制,應(yīng)用設(shè)備內(nèi)存無(wú)限制、編程容易,但其性能較其它幾種語(yǔ)言偏低,所以,Java性能的優(yōu)化一直處于持續(xù)推進(jìn)中。

表1 異構(gòu)系統(tǒng)編程環(huán)境與內(nèi)存空間模型
Java虛擬機(jī)(Java Virtual Machine,JVM)是其工作核心,也是性能調(diào)優(yōu)的重點(diǎn)。根據(jù)系統(tǒng)體系結(jié)構(gòu)采用指令級(jí)并行和多處理器并行,提供多線程擴(kuò)展,將Java線程和虛擬處理器間的對(duì)應(yīng)關(guān)系與虛擬和物理處理器及操作系統(tǒng)間的對(duì)應(yīng)關(guān)系分開處理[4]。通過監(jiān)視運(yùn)行情況確定其中運(yùn)行熱點(diǎn),再將熱點(diǎn)代碼段集中優(yōu)化編譯后直接運(yùn)行。用直接的內(nèi)存引用替代對(duì)象句柄,提高內(nèi)存分配效率[4]。用操作系統(tǒng)中的線程實(shí)現(xiàn)虛擬機(jī)中的Java線程,降低線程間的干擾,提供快速線程同步機(jī)制[4]。此外還采用即時(shí)編譯、動(dòng)態(tài)優(yōu)化[4]以及字節(jié)碼優(yōu)化等技術(shù)[6]。

表2 異構(gòu)系統(tǒng)編程環(huán)境及其性能、編程容易度比較
除JVM外,針對(duì)操作系統(tǒng)層次的性能,數(shù)據(jù)采集與監(jiān)控分析也是研究重點(diǎn)。其中,文獻(xiàn)[16]、[17]、[18]、[19]提出了資源監(jiān)控系統(tǒng)及其實(shí)現(xiàn),在操作系統(tǒng)層進(jìn)行資源消耗的數(shù)據(jù)采集,如CPU占用時(shí)間、CPU負(fù)載、內(nèi)存分配與占用、文件傳輸負(fù)載、網(wǎng)絡(luò)傳輸負(fù)載等。通過對(duì)這些數(shù)據(jù)進(jìn)行分析找到性能的瓶頸所在,并據(jù)此進(jìn)行優(yōu)化操作。文獻(xiàn)[20]則以Java支持的自動(dòng)垃圾回收機(jī)制的運(yùn)行環(huán)境為切入點(diǎn),深入研究?jī)?nèi)存泄露問題,并明確指出內(nèi)存泄漏的檢測(cè)應(yīng)是低侵入性、合理負(fù)載、不影響應(yīng)用與JVM的正常運(yùn)行。
Java應(yīng)用系統(tǒng)不僅涉及操作系統(tǒng)、JVM等底層,還與Web應(yīng)用、數(shù)據(jù)庫(kù)應(yīng)用等密切相關(guān),所以它們的優(yōu)化也是必須關(guān)注的。文獻(xiàn)[2]以JVM性能調(diào)優(yōu)為基礎(chǔ),實(shí)現(xiàn)了Java Servlet模式下的WebGIS服務(wù)器性能優(yōu)化。文獻(xiàn)[5]、[7]則提出了服務(wù)器端的優(yōu)化與Web前端程序代碼優(yōu)化相結(jié)合的方法,達(dá)到表現(xiàn)層的性能調(diào)優(yōu)目的。文獻(xiàn)[8]、[9]則通過研究結(jié)構(gòu)設(shè)計(jì)、內(nèi)存優(yōu)化、索引優(yōu)化、SQL優(yōu)化等方式,對(duì)數(shù)據(jù)庫(kù)的應(yīng)用性能進(jìn)行了調(diào)優(yōu)。
大部分性能優(yōu)化工作集中在底層,隨著計(jì)算機(jī)硬件體系和制造技術(shù)的進(jìn)步,以及JDK的不斷推陳出新,底層的性能優(yōu)化也不斷得到提高。但是,Java應(yīng)用系統(tǒng)的良好性能并不僅僅依靠底層的優(yōu)化,而是必須形成一個(gè)完整的體系。相關(guān)工作缺少系統(tǒng)優(yōu)化思想,各自為政,無(wú)法從整個(gè)系統(tǒng)角度調(diào)優(yōu)系統(tǒng)性能。所以,本文在此基礎(chǔ)上提出了一種自頂向下的Java應(yīng)用系統(tǒng)性能優(yōu)化方法,根據(jù)工程項(xiàng)目應(yīng)用開發(fā)特點(diǎn),從系統(tǒng)頂層設(shè)計(jì)開始進(jìn)行性能優(yōu)化,覆蓋了應(yīng)用層、容器層、數(shù)據(jù)持久化層、JVM層、操作系統(tǒng)與網(wǎng)絡(luò)層。該方法將貫穿于應(yīng)用項(xiàng)目設(shè)計(jì)之初直至應(yīng)用項(xiàng)目的生命周期終結(jié)為止,是一個(gè)綜合的系統(tǒng)工程。
自頂向下的Java應(yīng)用系統(tǒng)性能優(yōu)化方法對(duì)系統(tǒng)進(jìn)行整體性能優(yōu)化,充分考慮了各個(gè)層面的性能調(diào)優(yōu)以及它們之間的相互影響,并根據(jù)設(shè)計(jì)目標(biāo)與需求進(jìn)行平衡,取得系統(tǒng)整體性能最大化調(diào)優(yōu)結(jié)果。該方法整體結(jié)構(gòu)如圖1所示。

圖1 自頂向下Java應(yīng)用系統(tǒng)性能優(yōu)化方法結(jié)構(gòu)
系統(tǒng)性能優(yōu)化工作是全局性的而非局部性的,過去的一些優(yōu)化案例[3]表明,系統(tǒng)部署運(yùn)行以后進(jìn)行的性能分析與調(diào)優(yōu)可能會(huì)導(dǎo)致應(yīng)用程序修改,為避免此種情況出現(xiàn),在應(yīng)用系統(tǒng)設(shè)計(jì)之初就應(yīng)當(dāng)納入性能優(yōu)化的相應(yīng)工作,以良好的系統(tǒng)設(shè)計(jì)來(lái)規(guī)避許多潛在的性能問題,這便是應(yīng)用層的性能優(yōu)化。
應(yīng)用層的性能優(yōu)化包括系統(tǒng)設(shè)計(jì)優(yōu)化與Java編碼實(shí)現(xiàn)優(yōu)化兩部分。系統(tǒng)設(shè)計(jì)優(yōu)化又包含了軟件結(jié)構(gòu)設(shè)計(jì)優(yōu)化和算法設(shè)計(jì)優(yōu)化,良好的軟件結(jié)構(gòu)設(shè)計(jì)對(duì)系統(tǒng)的整體性能有著至關(guān)重要的作用,它的應(yīng)用會(huì)避免許多可能出現(xiàn)的性能問題。科學(xué)合理地使用設(shè)計(jì)模式將有助于形成良好的軟件結(jié)構(gòu)。例如:對(duì)于頻繁使用的那些重量級(jí)對(duì)象采用單例模式,可減少new操作的次數(shù),節(jié)約創(chuàng)建對(duì)象的時(shí)間,降低系統(tǒng)內(nèi)存的使用頻率。通過代理模式實(shí)現(xiàn)延遲加載,提高系統(tǒng)性能,加快系統(tǒng)的反應(yīng)速度。應(yīng)用享元模式復(fù)用重量級(jí)對(duì)象,節(jié)省重復(fù)創(chuàng)建對(duì)象帶來(lái)的開銷,減少創(chuàng)建對(duì)象的數(shù)量,優(yōu)化內(nèi)存結(jié)構(gòu)。
算法設(shè)計(jì)優(yōu)化根據(jù)應(yīng)用的實(shí)際需求,合理使用數(shù)據(jù)結(jié)構(gòu),科學(xué)平衡時(shí)間、空間開銷使之總體最優(yōu)。例如通過使用緩沖協(xié)調(diào)上層組件和下層組件的性能差,減少等待時(shí)間等。為防止密集型的I/O操作成為系統(tǒng)瓶頸,要考慮緩沖技術(shù)。在程序中使用數(shù)據(jù)庫(kù)連接池和線程池,只對(duì)重量級(jí)對(duì)象使用對(duì)象池技術(shù)。良好的算法和數(shù)據(jù)結(jié)構(gòu)的效率對(duì)應(yīng)用系統(tǒng)的性能優(yōu)化是有益的[10-11]。
Java編碼實(shí)現(xiàn)優(yōu)化需要程序員具備良好的個(gè)人編程習(xí)慣,正確應(yīng)用JDK API庫(kù)中的各類方法,編寫出高效精煉的代碼,讓應(yīng)用程序執(zhí)行更少的CPU指令,通過更短的執(zhí)行路徑實(shí)現(xiàn)程序功能,確保系統(tǒng)的整體最優(yōu)性能。例如使用最優(yōu)方法提高算法實(shí)現(xiàn)效率,使用StringBuilder代替字符串連接運(yùn)算符“+”,多使用棧,盡量避免應(yīng)用遞歸。遞歸非常消耗資源,在計(jì)算密集型的代碼中,要避免使用正則表達(dá)式。不要調(diào)用高開銷的方法,優(yōu)化自定義hasCode()方法和equals()方法。減少對(duì)共享資源的競(jìng)爭(zhēng)(鎖競(jìng)爭(zhēng))頻率,縮短鎖持有的時(shí)間等。
應(yīng)用層性能優(yōu)化不僅為算法產(chǎn)生更有效率的代碼,而且會(huì)降低GC頻率,減少GC壓力,間接促進(jìn)JVM的優(yōu)化。
容器層優(yōu)化涉及到開發(fā)和生產(chǎn)運(yùn)行兩大階段。開發(fā)階段需要采用一些應(yīng)用層編碼實(shí)現(xiàn)的優(yōu)化技術(shù),還應(yīng)注意根據(jù)實(shí)際工程項(xiàng)目需求分別進(jìn)行數(shù)據(jù)庫(kù)連接優(yōu)化、網(wǎng)絡(luò)訪問優(yōu)化、緩存應(yīng)用優(yōu)化、檢索優(yōu)化、文件的配置與訪問優(yōu)化等。開發(fā)階段的性能測(cè)試也不能忽視,例如訪問的壓力測(cè)試等。項(xiàng)目開發(fā)過程中選用適當(dāng)?shù)臏y(cè)試工具進(jìn)行性能測(cè)試,LoadRunner用于壓力測(cè)試,Jmeter用于性能測(cè)試。測(cè)試一些常用指標(biāo),如響應(yīng)時(shí)間、吞吐率、資源利用率、最大并發(fā)用戶數(shù)[12-13]等。而生產(chǎn)運(yùn)行階段,在硬件設(shè)備上需要采用與開發(fā)階段不同的配置,同時(shí)選用適當(dāng)?shù)臄?shù)據(jù)采集與監(jiān)聽工具,收集組合參數(shù)信息,根據(jù)這些信息分析判斷后再進(jìn)行配置調(diào)諧、監(jiān)聽收集信息的迭代過程,直至性能優(yōu)化滿意達(dá)標(biāo)為止。
容器本身的性能要受到配置的影響,正確合理的配置是容器性能的保證。以甲骨文公司的GlassFish為例,它有開發(fā)模式和生產(chǎn)模式兩種。開發(fā)模式會(huì)允許JSP自動(dòng)加載,檢查每個(gè)頁(yè)面是否有變化,開發(fā)者不用重新部署應(yīng)用程序就能看到運(yùn)行結(jié)果,所以開發(fā)模式以損失性能的代價(jià)換來(lái)了靈活性,它只適用于項(xiàng)目工程的開發(fā)階段。而在生產(chǎn)運(yùn)行階段,應(yīng)使容器配置為生產(chǎn)模式,它會(huì)關(guān)閉自動(dòng)加載功能,避免了系統(tǒng)調(diào)用檢查文件的時(shí)間戳,不會(huì)影響多線程并發(fā)訪問同一JSP文件時(shí)容器的處理能力,也不會(huì)影響其應(yīng)用擴(kuò)展性[3]。
在容器中的Web應(yīng)用程序開發(fā)部署中,根據(jù)已有項(xiàng)目實(shí)現(xiàn)經(jīng)驗(yàn),應(yīng)遵循以下規(guī)則:使用init方法緩存靜態(tài)數(shù)據(jù)和資源引用,如果靜態(tài)引用資源則采用JSP的include指令,如果包含資源動(dòng)態(tài)生成的響應(yīng),則用JSP的include標(biāo)簽。剔除JSP頁(yè)面模板中保留的空格,可減少通過網(wǎng)絡(luò)傳遞的文件大小,改善網(wǎng)絡(luò)傳輸性能。在JSP中應(yīng)用jsp:useBean,一般大多數(shù)情況下使用className屬性,只在絕對(duì)必要時(shí)才使用beanName[3]。
處于生產(chǎn)模式下的容器還需對(duì)其進(jìn)行綜合監(jiān)控,通過組合參數(shù)尋找問題點(diǎn)。在GlassFish應(yīng)用中,對(duì)具有1-2個(gè)CPU的開發(fā)計(jì)算機(jī)來(lái)說(shuō),設(shè)置線程池的最大數(shù)為5;但對(duì)于多核多CPU的生產(chǎn)計(jì)算機(jī),設(shè)置的線程池最大數(shù)應(yīng)為硬件線程數(shù)的2倍[3]。
容器層優(yōu)化的目的是消除性能瓶頸,充分利用系統(tǒng)資源。隨著用戶負(fù)載的增加,使應(yīng)用能夠進(jìn)行垂直擴(kuò)展和水平擴(kuò)展。
應(yīng)用系統(tǒng)進(jìn)行設(shè)計(jì)開發(fā)時(shí),需要持久化層的優(yōu)化。對(duì)于那些應(yīng)用Java框架的系統(tǒng),如Spring、Hibernate、MyBatis等,它們的性能表現(xiàn)完全依賴持久化層性能。例如:通過應(yīng)用優(yōu)化的鍵生成器,減少生成主鍵的代價(jià),使用JDBC批處理 inserts/updates減少來(lái)回傳輸,定期清理向數(shù)據(jù)庫(kù)添加或修改數(shù)據(jù)時(shí)保留的會(huì)話,使用二級(jí)查詢緩存等。
數(shù)據(jù)持久化層提供對(duì)象-關(guān)系映射功能,并在Java應(yīng)用中管理關(guān)系數(shù)據(jù)。采用Java領(lǐng)域模型的應(yīng)用程序,通過該映射與關(guān)系型數(shù)據(jù)庫(kù)交互。在此過程中,持久層的性能優(yōu)化涉及到緩存容量配置、線程池配置、數(shù)據(jù)庫(kù)鎖策略等幾個(gè)方面。
Java持久化API應(yīng)用的二級(jí)緩存容量會(huì)影響應(yīng)用程序的性能,如果應(yīng)用程序頻繁地訪問緩存,則會(huì)產(chǎn)生大量?jī)?nèi)存,導(dǎo)致JVM也會(huì)頻繁地進(jìn)行垃圾收集,反而降低了應(yīng)用程序性能。因此,合理配置緩存是關(guān)鍵。這是一個(gè)迭代過程,通過應(yīng)用系統(tǒng)運(yùn)行過程的數(shù)據(jù)采集,分析、計(jì)算、判斷緩存的命中率,據(jù)此進(jìn)行調(diào)節(jié)和合理設(shè)置。一般而言,其容量至少設(shè)置成事務(wù)使用的同類對(duì)象之和。線程池的配置則取決于調(diào)用模式,通用原則是:線程池的最小容量等于硬件線程數(shù)或虛擬處理器的數(shù)目,線程池的最大容量則等于硬件線程數(shù)或虛擬處理器數(shù)目的2倍。數(shù)據(jù)庫(kù)鎖策略需要根據(jù)項(xiàng)目工程的實(shí)際需求進(jìn)行選擇并保證數(shù)據(jù)的完整性。如果應(yīng)用系統(tǒng)存在大量的訪問用戶,頻繁地訪問并更新數(shù)據(jù),那么采用悲觀鎖能獲得較好性能,避免了大量的事務(wù)回滾以及并發(fā)訪問。反之,如果數(shù)據(jù)不被并發(fā)事務(wù)頻繁地修改,則適合采用樂觀鎖[3]。
數(shù)據(jù)持久化層的優(yōu)化工作也必須兼顧開發(fā)與生產(chǎn)運(yùn)行階段,不同階段采用相應(yīng)的策略才能最大限度地獲取系統(tǒng)的優(yōu)良性能。
JVM為了滿足各種應(yīng)用需要,為程序運(yùn)行提供了大量配置選項(xiàng),但是這些選項(xiàng)并非對(duì)所有的Java應(yīng)用都是最優(yōu)的,某些配置選項(xiàng)對(duì)某類應(yīng)用是最優(yōu)的,然而對(duì)另外一些應(yīng)用卻未必最優(yōu)。所以,JVM的配置選項(xiàng)有很強(qiáng)的針對(duì)性。在實(shí)際優(yōu)化操作過程中,需要準(zhǔn)確獲取應(yīng)用系統(tǒng)運(yùn)行時(shí)的相關(guān)變化情況,它們會(huì)對(duì)JVM的優(yōu)化產(chǎn)生直接影響。
JVM的優(yōu)化需要全盤折衷考慮,它面臨著牽一發(fā)而動(dòng)全身的情況。因此,對(duì)現(xiàn)代JVM進(jìn)行調(diào)優(yōu)是一門藝術(shù)[3],往往滿足了系統(tǒng)的某個(gè)需求常常會(huì)犧牲系統(tǒng)的另一方面需求。例如減少了內(nèi)存消耗,卻影響了系統(tǒng)的吞吐量以及系統(tǒng)延遲;減少應(yīng)用程序部署使用的JVM數(shù)量又犧牲了應(yīng)用程序的可用性[3]。所以,對(duì)于不同的應(yīng)用系統(tǒng)因?yàn)閭?cè)重點(diǎn)不同, JVM的優(yōu)化也是完全不同的。
常規(guī)做法是針對(duì)具體的應(yīng)用系統(tǒng),根據(jù)其性能測(cè)試結(jié)果不斷優(yōu)化配置,反復(fù)進(jìn)行迭代,直到這一過程取得令人滿意的指標(biāo)結(jié)果為止。
對(duì)操作系統(tǒng)進(jìn)行性能監(jiān)控,收集各類相關(guān)數(shù)據(jù)并進(jìn)行性能分析,根據(jù)分析結(jié)果采取對(duì)應(yīng)的優(yōu)化措施。
CPU使用率一般分為用戶態(tài)使用率和系統(tǒng)態(tài)使用率[3]。當(dāng)應(yīng)用執(zhí)行操作系統(tǒng)調(diào)用的時(shí)間占總的CPU應(yīng)用時(shí)間的百分比較低時(shí),或者操作系統(tǒng)的共享資源無(wú)競(jìng)爭(zhēng)或低競(jìng)爭(zhēng)、I/O設(shè)備之間的交互較少時(shí),降低CPU系統(tǒng)態(tài)使用率,升高CPU用戶態(tài)使用率,以提高應(yīng)用系統(tǒng)性能。所以,為達(dá)到性能最佳,應(yīng)盡可能降低CPU系統(tǒng)態(tài)使用率。
通過監(jiān)控內(nèi)存的相關(guān)屬性實(shí)時(shí)獲取該資源的消耗情況,以便采取應(yīng)對(duì)措施。例如系統(tǒng)在進(jìn)行頁(yè)面交換或使用虛擬內(nèi)存時(shí),Java應(yīng)用或JVM就會(huì)出現(xiàn)性能問題,發(fā)生磁盤與內(nèi)存之間的置換會(huì)影響應(yīng)用的響應(yīng)和吞吐量[3,15,19]。
網(wǎng)絡(luò)I/O的性能則會(huì)影響Java應(yīng)用的性能與擴(kuò)展。系統(tǒng)運(yùn)行過程中的優(yōu)化必須進(jìn)行網(wǎng)絡(luò)I/O監(jiān)控,通過采集的數(shù)據(jù)計(jì)算出網(wǎng)絡(luò)I/O的使用率并采取相應(yīng)措施。例如減少網(wǎng)絡(luò)讀寫的系統(tǒng)調(diào)用,減少處理請(qǐng)求和發(fā)送響應(yīng)的線程數(shù)以改善性能。
Java應(yīng)用系統(tǒng)的性能優(yōu)化是一項(xiàng)貫穿于項(xiàng)目設(shè)計(jì)之初直至項(xiàng)目生命周期終結(jié)的綜合系統(tǒng)工程,需充分考慮各個(gè)層面的性能調(diào)優(yōu)以及它們之間的相互影響,并根據(jù)設(shè)計(jì)目標(biāo)與需求進(jìn)行平衡。前三層的優(yōu)化是應(yīng)用系統(tǒng)開發(fā)階段的重點(diǎn),圍繞高效算法的設(shè)計(jì)進(jìn)行,同時(shí)兼顧系統(tǒng)的恰當(dāng)配置。后兩層優(yōu)化則根據(jù)應(yīng)用系統(tǒng)的實(shí)際需求與擁有的計(jì)算資源進(jìn)行綜合性能調(diào)優(yōu)。在自頂向下的方法中,通過前三層的優(yōu)化設(shè)計(jì)降低后兩層的調(diào)優(yōu)難度,而后兩層的調(diào)優(yōu)則需要避免對(duì)前三層的代碼進(jìn)行修改。在后續(xù)工作中,應(yīng)深入開展多層性能優(yōu)化的理論模型研究,通過創(chuàng)建模型,精確計(jì)算與評(píng)估應(yīng)用系統(tǒng)的性能優(yōu)化程度,以定量分析的形式完善應(yīng)用系統(tǒng)性能優(yōu)化。