国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開(kāi)發(fā)設(shè)計(jì) > 正文

Java性能優(yōu)化

2019-11-18 13:22:46
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

  java語(yǔ)言非凡強(qiáng)調(diào)準(zhǔn)確性,但可靠的行為要以性能作為代價(jià)。這一特點(diǎn)反映在自動(dòng)收集垃圾、嚴(yán)格的運(yùn)行期檢查、完整的字節(jié)碼檢查以及保守的運(yùn)行期同步等等方面。對(duì)一個(gè)解釋型的虛擬機(jī)來(lái)說(shuō),由于目前有大量平臺(tái)可供挑選,所以進(jìn)一步阻礙了性能的發(fā)揮。
  “先做完它,再逐步完善。幸好需要改進(jìn)的地方通常不會(huì)太多。”(Steve McConnell的《About performance》[16])
  本附錄的宗旨就是指導(dǎo)大家尋找和優(yōu)化“需要完善的那一部分”。
  
  1 基本方法
  只有正確和完整地檢測(cè)了程序后,再可著手解決性能方面的問(wèn)題:
  (1) 在現(xiàn)實(shí)環(huán)境中檢測(cè)程序的性能。若符合要求,則目標(biāo)達(dá)到。若不符合,則轉(zhuǎn)到下一步。
  (2) 尋找最致命的性能瓶頸。這也許要求一定的技巧,但所有努力都不會(huì)白費(fèi)。如簡(jiǎn)單地猜測(cè)瓶頸所在,并試圖進(jìn)行優(yōu)化,那么可能是白花時(shí)間。
  (3) 運(yùn)用本附錄介紹的提速技術(shù),然后返回步驟1。
  
  為使努力不至白費(fèi),瓶頸的定位是至關(guān)重要的一環(huán)。Donald Knuth[9]曾改進(jìn)過(guò)一個(gè)程序,那個(gè)程序把50%的時(shí)間都花在約4%的代碼量上。在僅一個(gè)工作小時(shí)里,他修改了幾行代碼,使程序的執(zhí)行速度倍增。此時(shí),若將時(shí)間繼續(xù)投入到剩余代碼的修改上,那么只會(huì)得不償失。Knuth在編程界有一句名言:“過(guò)早的優(yōu)化是一切麻煩的根源”(PRemature optimization is the root of all evil)。最明智的做法是抑制過(guò)早優(yōu)化的沖動(dòng),因?yàn)槟菢幼隹赡苓z漏多種有用的編程技術(shù),造成代碼更難理解和操控,并需更大的精力進(jìn)行維護(hù)。
  
  2 尋找瓶頸
  為找出最影響程序性能的瓶頸,可采取下述幾種方法:
  
  2.1 安插自己的測(cè)試代碼
  插入下述“顯式”計(jì)時(shí)代碼,對(duì)程序進(jìn)行評(píng)測(cè):
  
  long start = System.currentTimeMillis();
  // 要計(jì)時(shí)的運(yùn)算代碼放在這兒
  long time = System.currentTimeMillis() - start;
  
  利用System.out.println(),讓一種不常用到的方法將累積時(shí)間打印到控制臺(tái)窗口。由于一旦出錯(cuò),編譯器會(huì)將其忽略,所以可用一個(gè)“靜態(tài)最終布爾值”(Static final boolean)打開(kāi)或關(guān)閉計(jì)時(shí),使代碼能放心留在最終發(fā)行的程序里,這樣任何時(shí)候都可以拿來(lái)應(yīng)急。盡管還可以選用更復(fù)雜的評(píng)測(cè)手段,但若僅僅為了量度一個(gè)特定任務(wù)的執(zhí)行時(shí)間,這無(wú)疑是最簡(jiǎn)便的方法。
  System.currentTimeMillis()返回的時(shí)間以千分之一秒(1毫秒)為單位。然而,有些系統(tǒng)的時(shí)間精度低于1毫秒(如Windows PC),所以需要重復(fù)n次,再將總時(shí)間除以n,獲得準(zhǔn)確的時(shí)間。
  
  2.2 JDK性能評(píng)測(cè)[2]
  JDK配套提供了一個(gè)內(nèi)建的評(píng)測(cè)程序,能跟蹤花在每個(gè)例程上的時(shí)間,并將評(píng)測(cè)結(jié)果寫入一個(gè)文件。不幸的是,JDK評(píng)測(cè)器并不穩(wěn)定。它在JDK 1.1.1中能正常工作,但在后續(xù)版本中卻非常不穩(wěn)定。
  為運(yùn)行評(píng)測(cè)程序,請(qǐng)?jiān)谡{(diào)用Java解釋器的未優(yōu)化版本時(shí)加上-prof選項(xiàng)。例如:
  java_g -prof myClass
  或加上一個(gè)程序片(Applet):
  java_g -prof sun.applet.AppletViewer applet.Html
  理解評(píng)測(cè)程序的輸出信息并不輕易。事實(shí)上,在JDK 1.0中,它居然將方法名稱截短為30字符。所以可能無(wú)法區(qū)分出某些方法。然而,若您用的平臺(tái)確實(shí)能支持-prof選項(xiàng),那么可試試Vladimir Bulatov的“HyperPorf”[3]或者Greg White的“ProfileViewer”來(lái)解釋一下結(jié)果。
  
  2.3 非凡工具
  假如想隨時(shí)跟上性能優(yōu)化工具的潮流,最好的方法就是作一些Web站點(diǎn)的常客。比如由Jonathan Hardwick制作的“Tools for Optimizing Java”(Java優(yōu)化工具)網(wǎng)站:
  http://www.cs.cmu.edu/~jch/java/tools.html
  
  2.4 性能評(píng)測(cè)的技巧
  ■由于評(píng)測(cè)時(shí)要用到系統(tǒng)時(shí)鐘,所以當(dāng)時(shí)不要運(yùn)行其他任何進(jìn)程或應(yīng)用程序,以免影響測(cè)試結(jié)果。
  ■如對(duì)自己的程序進(jìn)行了修改,并試圖(至少在開(kāi)發(fā)平臺(tái)上)改善它的性能,那么在修改前后應(yīng)分別測(cè)試一下代碼的執(zhí)行時(shí)間。
  ■盡量在完全一致的環(huán)境中進(jìn)行每一次時(shí)間測(cè)試。
  ■假如可能,應(yīng)設(shè)計(jì)一個(gè)不依靠任何用戶輸入的測(cè)試,避免用戶的不同反應(yīng)導(dǎo)致結(jié)果出現(xiàn)誤差。
  
  3 提速方法
  現(xiàn)在,要害的性能瓶頸應(yīng)已隔離出來(lái)。接下來(lái),可對(duì)其應(yīng)用兩種類型的優(yōu)化:常規(guī)手段以及依靠Java語(yǔ)言。
  
  3.1 常規(guī)手段
  通常,一個(gè)有效的提速方法是用更現(xiàn)實(shí)的方式重新定義程序。例如,在《Programming Pearls》(編程拾貝)一書中[14],Bentley利用了一段小說(shuō)數(shù)據(jù)描寫,它可以生成速度非常快、而且非常精簡(jiǎn)的拼寫檢查器,從而介紹了Doug McIlroy對(duì)英語(yǔ)語(yǔ)言的表述。除此以外,與其他方法相比,更好的算法也許能帶來(lái)更大的性能提升——非凡是在數(shù)據(jù)集的尺寸越來(lái)越大的時(shí)候。欲了解這些常規(guī)手段的詳情,請(qǐng)參考本附錄末尾的“一般書籍”清單。
  
  3.2 依靠語(yǔ)言的方法
  為進(jìn)行客觀的分析,最好明確把握各種運(yùn)算的執(zhí)行時(shí)間。這樣一來(lái),得到的結(jié)果可獨(dú)立于當(dāng)前使用的計(jì)算機(jī)——通過(guò)除以花在本地賦值上的時(shí)間,最后得到的就是“標(biāo)準(zhǔn)時(shí)間”。
  
  運(yùn)算 示例 標(biāo)準(zhǔn)時(shí)間
  
  本地賦值 i=n; 1.0
  實(shí)例賦值 this.i=n; 1.2
  int增值 i++; 1.5
  byte增值 b++; 2.0
  short增值 s++; 2.0
  float增值 f++; 2.0
  double增值 d++; 2.0
  空循環(huán) while(true) n++; 2.0
  三元表達(dá)式 (x<0) ?-x : x 2.2
  算術(shù)調(diào)用 Math.abs(x); 2.5
  數(shù)組賦值 a[0] = n; 2.7
  long增值 l++; 3.5
  方法調(diào)用 funct(); 5.9
  throw或catch異常 try{ throw e; }或catch(e){} 320
  同步方法調(diào)用 synchMehod(); 570
  新建對(duì)象 new Object(); 980
  新建數(shù)組 new int[10]; 3100
  
  通過(guò)自己的系統(tǒng)(如我的Pentium 200 Pro,Netscape 3及JDK 1.1.5),這些相對(duì)時(shí)間向大家揭示出:新建對(duì)象和數(shù)組會(huì)造成最沉重的開(kāi)銷,同步會(huì)造成比較沉重的開(kāi)銷,而一次不同步的方法調(diào)用會(huì)造成適度的開(kāi)銷。參考資源[5]和[6]為大家總結(jié)了測(cè)量用程序片的Web地址,可到自己的機(jī)器上運(yùn)行它們。
  
  1. 常規(guī)修改
  下面是加快Java程序要害部分執(zhí)行速度的一些常規(guī)操作建議(注重對(duì)比修改前后的測(cè)試結(jié)果)。
  
  將... 修改成... 理由
  
  接口 抽象類(只需一個(gè)父時(shí)) 接口的多個(gè)繼續(xù)會(huì)妨礙性能的優(yōu)化
  非本地或數(shù)組循環(huán)變量 本地循環(huán)變量 根據(jù)前表的耗時(shí)比較,一次實(shí)例整數(shù)賦值的時(shí)間是本地整數(shù)賦值時(shí)間的1.2倍,但數(shù)組賦值的時(shí)間是本地整數(shù)賦值的2.7倍
  鏈接列表(固定尺寸) 保存丟棄的鏈接項(xiàng)目,或?qū)⒘斜硖鎿Q成一個(gè)循環(huán)數(shù)組(大致知道尺寸) 每新建一個(gè)對(duì)象,都相當(dāng)于本地賦值980次。參考“重復(fù)利用對(duì)象”(下一節(jié))、Van Wyk[12] p.87以及Bentley[15] p.81
  x/2(或2的任意次冪) X>>2(或2的任意次冪) 使用更快的硬件指令
  
  3.3 非凡情況
  ■字串的開(kāi)銷:字串連接運(yùn)算符+看似簡(jiǎn)單,但實(shí)際需要消耗大量系統(tǒng)資源。編譯器可高效地連接字串,但變量字串卻要求可觀的處理器時(shí)間。例如,假設(shè)s和t是字串變量:
  System.out.println("heading" + s + "trailer" + t);
  上述語(yǔ)句要求新建一個(gè)StringBuffer(字串緩沖),追加自變量,然后用toString()將結(jié)果轉(zhuǎn)換回一個(gè)字串。因此,無(wú)論磁盤空間還是處理器時(shí)間,都會(huì)受到嚴(yán)重消耗。若預(yù)備追加多個(gè)字串,則可考慮直接使用一個(gè)字串緩沖——非凡是能在一個(gè)循環(huán)里重復(fù)利用它的時(shí)候。通過(guò)在每次循環(huán)里禁止新建一個(gè)字串緩沖,可節(jié)省980單位的對(duì)象創(chuàng)建時(shí)間(如前所述)。利用substring()以及其他字串方法,可進(jìn)一步地改善性能。假如可行,字符數(shù)組的速度甚至能夠更快。也要注重由于同步的關(guān)系,所以StringTokenizer會(huì)造成較大的開(kāi)銷。
  ■同步:在JDK解釋器中,調(diào)用同步方法通常會(huì)比調(diào)用不同步方法慢10倍。經(jīng)JIT編譯器處理后,這一性能上的差距提升到50到100倍(注重前表總結(jié)的時(shí)間顯示出要慢97倍)。所以要盡可能避免使用同步方法——若不能避免,方法的同步也要比代碼塊的同步稍快一些。
  ■重復(fù)利用對(duì)象:要花很長(zhǎng)的時(shí)間來(lái)新建一個(gè)對(duì)象(根據(jù)前表總結(jié)的時(shí)間,對(duì)象的新建時(shí)間是賦值時(shí)間的980倍,而新建一個(gè)小數(shù)組的時(shí)間是賦值時(shí)間的3100倍)。因此,最明智的做法是保存和更新老對(duì)象的字段,而不是創(chuàng)建一個(gè)新對(duì)象。例如,不要在自己的paint()方法中新建一個(gè)Font對(duì)象。相反,應(yīng)將其聲明成實(shí)例對(duì)象,再初始化一次。在這以后,可在paint()里需要的時(shí)候隨時(shí)進(jìn)行更新。參見(jiàn)Bentley編著的《編程拾貝》,p.81[15]。
  ■異常:只有在不正常的情況下,才應(yīng)放棄異常處理模塊。什么才叫“不正常”呢?這通常是指程序碰到了問(wèn)題,而這一般是不愿見(jiàn)到的,所以性能不再成為優(yōu)先考慮的目標(biāo)。進(jìn)行優(yōu)化時(shí),將小的“try-catch”塊合并到一起。由于這些塊將代碼分割成小的、各自獨(dú)立的片斷,所以會(huì)妨礙編譯器進(jìn)行優(yōu)化。另一方面,若過(guò)份熱衷于刪除異常處理模塊,也可能造成代碼健壯程度的下降。
  ■散列處理:首先,Java 1.0和1.1的標(biāo)準(zhǔn)“散列表”(Hashtable)類需要造型以及非凡消耗系統(tǒng)資源的同步處理(570單位的賦值時(shí)間)。其次,早期的JDK庫(kù)不能自動(dòng)決定最佳的表格尺寸。最后,散列函數(shù)應(yīng)針對(duì)實(shí)際使用項(xiàng)(Key)的特征設(shè)計(jì)。考慮到所有這些原因,我們可非凡設(shè)計(jì)一個(gè)散列類,令其與特定的應(yīng)用程序配合,從而改善常規(guī)散列表的性能。注重Java 1.2集合庫(kù)的散列映射(HashMap)具有更大的靈活性,而且不會(huì)自動(dòng)同步。
  ■方法內(nèi)嵌:只有在方法屬于final(最終)、private(專用)或static(靜

上一篇:用JAVA解壓ZIP

下一篇:抽象的進(jìn)步

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 临泉县| 山西省| 台湾省| 宁国市| 德令哈市| 商南县| 保定市| 子洲县| 泗阳县| 西宁市| 沽源县| 东明县| 宁陵县| 武川县| 周宁县| 津南区| 柏乡县| 高碑店市| 青田县| 安阳县| 鸡东县| 逊克县| 木里| 方城县| 庄河市| 绥阳县| 黑山县| 龙川县| 延川县| 隆化县| 宜兰市| 玉树县| 玛曲县| 台中市| 桂平市| 安阳市| 巫溪县| 平陆县| 五大连池市| 西乌珠穆沁旗| 颍上县|