熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> JSP教程 >> 正文

兩種方法定位Java應用程序瓶頸(2)

2013-11-15 11:42:02  來源: JSP教程 

  繼續最優化代碼
  在上面的剖析輸出中最頂端的項起源於GUI事件循環一旦我們使GUI對象可見(即調用setVisible(true))事件循環就開始了這個循環將持續應用的整個生命周期而且可以注意到在每一文件讀取之後應用就會刷新圖形這一點在文件數目較大時更為重要假設我們並不想查看中間的圖形(就是說這並不是一個公司的需求)通過將圖形顯示移動到main()的末尾我們就可以加快應用的速度那麼事件循環直到應用的最後一刻才會啟動paint()也將很可能只需要被調用兩次
  關於這一點我們可能會產生這樣的疑問圖形顯示是否需要?它是否需要與應用同時顯示?根據所做的回答我們或許會將圖形移動到另一線程另一應用甚至另一機器為了便於討論我們假設需要即時的圖形顯示
  修改後的main()如下:
   public static void main (String[] argv)
   throws IOException {
   Letters letters = new Letters();
   long startTime = SystemcurrentTimeMillis();
  
   for (int i=; i
  修改後進行測試產生如下結果
  ● 平台 A: 時間在 秒之間
  ● 平台B: 時間在 秒之間
  我們已經大大改善了性能但仍然沒有達到我們的性能目標
  附加的剖析顯示
  CPU SAMPLES BEGIN (total = ) Sun Jul ::
  rank self accum count trace method
   % % javaioFileInputStreamreadBytes
   % % sunawtwindowsWToolkitinit
   % % sunawtwindowsWToolkiteventLoop
  這和我們前面所看到的明顯不同我們花費的時間主要集中在readBytes()上對readBytes()我們該怎麼辦呢?
  JDK 引入了javanio 包中一整套新類和它用於緩沖區I/O (輸入/輸出)的子包有一個類MappedByteBuffer看起來特別有用我們可以使用MappedByteBuffer 對象在內存中高效表征一個文件的內容這個類對緩沖區和內存管理的詳細信息進行操作
  為了把MappedByteBuffer對象連接到文件我們將使用FileChannel對象 FileChannel 表征緩沖區與可進行讀映射和處理文件的文件之間一個可靠的線程連接FileChannel對象保留文件當前位置信息並提供低級特定操作系統的最優化JDK 文檔包含了如何使用FileChannel和MappedByteBuffer的例子
  使用 FileChannel 和 MappedByteBuffer修改代碼
  
  import javanioMappedByteBuffer;
  import javaniochannelsFileChannel;
  
   void countCharacters (String filename)
   throws IOException {
   FileInputStream fis = new FileInputStream(filename);
   FileChannel fc = fisgetChannel();
  // Get the files size and then map it into memory
   int sz = (int)fcsize();
   MappedByteBuffer bb = fcmap(FileChannelMapModeREAD_ONLY sz);
  
   for (int i=; i= ) && (pos <= 25)) {
   ++countArray[pos];
   }
   }
   fc.close();
   }
  ...
  修改後產生如下結果:
  ● 平台 A: 時間在2.1 和 4.7 秒之間
  ● 平台B: 時間在11 和13.8 秒之間
  這個結果較前一例子有了一些改善。tW.winGWIT.COM然而,從統計或者可感知角度來說,這可能並不重要。
  好了,另一個問題:我們應該如何達到我們的性能目標呢?這些文檔能不能在10秒左右的時間內處理完畢?我們已經離這一目標很近了。我們將再嘗試進行另一最優化。
  我們來看一下 FileChannel.map()文檔,描述的底端附近有一引人注意的引用。
  對多數操作系統而言,將一文件映射到內存中比通過常用的讀寫方法來讀寫好多字節數據代價更高。從性能角度來說,將相對較大的文件映射到內存中才是值得的。
  ByteBuffer 類,是MappedByteBuffer's 超類, 提供了java.nio 包中的可緩沖輸入。我們試著使用ByteBuffer而不是MappedByteBuffer 來讀取數據看看會發生什麼事情。
  ByteBuffer類的兩種方法可以獲得ByteBuffer: 方法allocate(int)和 allocateDirect(int)。這兩種方法的關鍵就是緩沖區的大小。 allocateDirect() 產生一個以字節為單位的直接型緩沖區,它能盡可能的執行本地文件的I/O。 allocate()產生一個以字節為單位的非直接型緩沖區,它交換少量的本地代碼用於具有決定性的行為(很可能會慢一些)。Sun推薦將以字節為單位的直接型緩沖區用於與大型文件相關並具有較長生命周期的緩沖區。
  我們修改後的代碼使用以字節為單位的直接型緩沖區:
  ...import java.nio.ByteBuffer;
  ...
   void countCharacters (String filename)
   throws IOException {
   FileInputStream fis = new FileInputStream(filename);
   FileChannel fc = fis.getChannel();
  // Get the file's size and then map it into memory
   int sz = (int)fc.size();
   int bufferSize = 1024;
   ByteBuffer bb = ByteBuffer.allocateDirect (bufferSize);
   int nbytes = -1;
   while ((nbytes = fc.read (bb)) != -1) {
   bb.rewind();
   for (int i=0; i= 0) && (pos <= 25)) {
   ++countArray[pos];
   }
   }
   }
   fc.close();
   }
  ...
  這一代碼運行時間結果:
  ● 平台 A: 時間在2.7 和 5.2 秒之間
  ● 平台B: 時間在13.1 和13.9 秒之間
   使用 allocateDirect()替換allocate()將以20%的比例降低性能。性能比前一例子會少慢一些。代碼也不是很容易閱讀。可是,很有意思的一點,我在一個具有大約20個Java源文件的目錄上使用相同的代碼進行試驗,結果得到了相反的結果。使用直接ByteBuffer編寫的代碼其速度要比使用MappedByteBuffer編寫的代碼快將近20%。這很可能與緩沖區和同步本地文件I/O操作所產生的附加花費有關。
  所以…那一種最優化策略最好呢?這取決於應用是如何加以使用的。根據本文開始列出的需求,我將選擇MappedByteBuffer版本。在我們的測試用例中它是最快的一個而且可以很好進行平衡。
  重要的問題是:在我們進行最後一輪最優化測試之前,性能是否足夠好了?如果已經足夠好了,我們只是在花費額外時間進行並無直接好處的優化。如果性能仍然不令人滿意,接下來的優化嘗試很可能會更困難而且也十分耗時。
  剖析如此重要的原因就是它告訴您要解決問題的信息,而且同時告訴您是否已經做到了這一點。有時候,花時間修改代碼對性能並沒有顯著的影響。實際上或許僅僅使您的代碼更難於閱讀和維護。而且,您可能會對HotSpot VM 的優化策略感到意外-您不知道如何使Java程序運行的更快。
  相同的改善流程對內存相關和I/O相關的性能問題一樣有效。內存相關-內存漏洞和碎片帳集通常是最難追蹤的。這是因為它們難於復制e而且觀測程序或許會改變內存相關的行為。
  改善性能的其他方法
  一旦剖析確定了性能問題的位置,決定做什麼仍然是需要小心處理的。這裡的一些建議可用於解決特定的性能問題:
  ● 方法調用:優化方法,或者盡量少調用;
  ● 類讀取:預先讀取類,使用懶散的(命令觸發)實例。注意:多線程程序中,避免使用兩次檢查的鎖定(請參見Brian Goetz's "Double-Checked Locking: Clever, but Broken" (JavaWorld, February 2001)查明原因);
  ● 在內存外運行: 共享對象而不是創建對象,可以使用對象工廠或者對象池;
  ● 字符串處理: 使用 StringBuffer 或者 char[]而不是String;
  ● 遞歸:消除遞歸方法調用並將其轉換成迭代體
  ● I/O 和序列: 確保I/O是使用緩沖方法的。考慮使用非同步方法編寫您自己的I/O子類;
  ● Collection類:使用滿足應用的collection類,有時數組的訪問和修改比collection更快。
  性能改善的另一方法:使用字節型代碼優化器。這些程序檢查類文件字節代碼,刪除未被使用的代碼,清空方法調用堆棧等。優化器最好的一點就是您不必修改任何代碼-您只需測試一下看看優化器是否正常工作。令人不是很滿意的地方就是優化器產生不可移植的代碼或代碼並未按照您所期望的方式工作。
  調整程序
  可感知性能比實際性能更重要。在您開始調整之前,決定如何判定什麼時候性能就是足夠好了。需要調整時,測試並剖析。如果它們還是有區別的,新的1.4 I/O 類可提供性能改善功能。剖析、剖析、再剖析。高效使用您的時間。不要更改代碼而且在獲得滿意的性能之後就不必再調整。
  
  
  
  
  
  

From:http://tw.wingwit.com/Article/program/Java/JSP/201311/19458.html
  • 上一篇文章:

  • 下一篇文章:
  • 推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.