java性能優化筆記(二)設計優化

設計優化手段:

  • 設計模式:使用常用設計模式改善代碼,優化運行時產生的對象、對象生命週期等。

  • Value-Object:合併網絡請求,減少網絡請求。

  • 業務代理:對業務模塊封裝代理層,代理層緩存遠程調用的請求數據。

  • 緩存和緩衝區:緩存熱區數據,對延時比較大的功能使用緩衝技術。

  • 池技術:使用線程池、連接池、對象池等技術減少對象、連接的重新創建。

  • 並行化:充分利用cpu資源,採用多進程、多線程、協程等技術。

  • 負載均衡:分發請求,減小單機壓力。

  • 時間和空間:適當情況下可以以內存或硬盤空間換取程序執行效率,同樣也可以降低程序執行效率來節省內存或硬盤空間。

性能優化可採用的設計模式:

  • 單例模式:保證應用中只存在一份對象實例,減少對象產生,節省內存空間,gc回收壓力減輕。

  • 享元模式:多業務模塊共享對象實例,減少對象產生,節省內存空間,gc回收壓力減輕。

  • 工廠模式:採用工廠生產對象實例,保證對象實例在適當的情況下創建,間接性優化對象生命週期。

  • 代理模式:封裝代理模塊,可對被代理對象進行安全檢查、緩存、對象實延遲實例化、延遲建立網絡連接或磁盤I/O操作等。

  • 裝飾者模式:在對象構建時包裝對象,最常見的是Java的流操作BufferedInputStream包裝FileInputStream進行緩衝操作。

  • 觀察者模式:在業務中,若A依賴B的處理狀態時,可用觀察者模式替代A對B的狀態輪詢,B會以回調的方式通知A。

Value-Object:

常見的Value-Object場景:循環中查詢數據庫,可改成集中一次性查詢(如:把id用逗號分隔,使用in或exists去查詢),然後通過Map(id作爲key關聯)以Value-Object的形式對應取出查詢結果。

業務代理模式:

在RPC調用中我們可以使用代理對模塊拆分,將代理提供的接口暴露給客戶端調用,代理中可對客戶端的重複調用進行緩存來減少網絡請求,或將多個RPC調用進行合併,也可以將PRC直接的調用依賴拆分,以事件驅動方式處理返回結果。同樣也可以在代理中做適當的安全性校驗。

緩存和緩衝區:

  • 緩存:最常用的緩存技術有 

    • 應用內緩存:jvm或容器內的緩存,通常可以用application實現,當然也有專業的緩存框架,如oscache、ehcache等,當然也可以使用HashMap自己實現。

    • 全局緩存:全局性的緩存服務,如redis、memchached等,本類型的緩存基本都是獨立的緩存服務器,應用通過api訪問緩存。由於單獨的緩存服務,所以不受應用內存或jvm內存大小的限制,也可以方便動彈的橫向擴容。

  • 緩衝區:常見的緩衝區技術如java的文件流讀寫緩衝BufferedInputStream或BufferedOutpuStream,將字節流緩衝到內存中逐漸寫入硬盤。

池技術:

  • 池分類:線程池、對象池、連接池。

  • 池實現:

    • newCachedThreadPool:創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閒線程,若無可回收,則新建線程。線程池爲無限大,當執行第二個任務時第一個任務已經完成,會複用執行第一個任務的線程,而不用每次新建線程。

    • newFixedThreadPool:創建一個定長線程池,可控制線程最大併發數,超出的線程會在隊列中等待。定長線程池的大小最好根據系統資源進行設置。如Runtime.getRuntime().availableProcessors()。

    • newScheduledThreadPool:創建一個定長線程池,支持定時及週期性任務執行。

    • newSingleThreadExecutor:創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。

    • 線程池:java的Executors提供四種線程池:

    • 對象池:一般可自己使用Vector實現,或使用apache的common-pools實現。也可以通過全局緩存實現對象池。

    • 連接池:數據庫的連接池主要有c3p0、dbcp等。

並行化:

  • 多進程:通過操作系統提供的fork和exec系統調用產生子進程,主進程與子進程之間採用共享內存、消息隊列、管道、信號、unix域內套接字等方式通信。多進程雖然佔用內存比較大(子進程會複製父進程的堆棧段、數據空間),但處理速度比較快。注意java中沒有多進程概念。

  • 多線程:充分利用cpu時間片,將進程劃分更細小的線程(子線程只得到了父線程的代碼段),由於只得到了代碼段,所以要比多進程節省空間,但由於得到的少自然在線程之間要共享數據。由此鎖是必不可少的,同時線程間通信還有:信號量、信號、條件。

  • 協程:將線程再進行更細小的切分,協程擁有自己的寄存器上下文和棧。協程調度切換時,將寄存器上下文和棧保存到其他地方,在切回來的時候,恢復先前保存的寄存器上下文和棧(狀態寄存器)。

負載均衡:

  • 常用負載技術: 

    • 硬負載:F5、思科、梭子魚、AppDirector系列。

    • 軟負載:LVS(4層負載)、nginx(7層負載)、apache(7層負載)、HAProxy(4-7層負載)等。這裏主要說明軟負載技術。

  • 負載策略:基於權重(weight)、輪詢(round robin)、hash ip算法、主從(master-slave)。

  • 軟負載方案:nginx+tomcat、apache+tomcat,需要注意session問題: 

    • 黏性session:session通過hash算法綁定在一臺tomcat上。一臺宕機會丟失session,出現單機故障。

    • session複製:tomcat之間廣播同步session,併發大以後容易產生廣播風暴。

    • 緩存共享session:可使用nginx的memcached session緩存模塊或Terracotta服務。也可以自己通過token/jsessionid+緩存/數據庫實現session管理。

時間和空間:

  • 時間換空間:最常見的例子是在冒泡排序中不適用第三個臨時變量交換前後兩個變量,請參考如下代碼:

    • 使用第三個臨時變量: 
      a=tmp; 
      a=b; 
      b=tmp;

    • 不使用第三個變量: 
      a=a+b; 
      b=a-b; 
      a=a-b;

  • 空間換時間:最典型的例子是通過緩存來加快熱數據的讀取速度,或使用對象池減小對象創建和銷燬的開銷。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章