JAVA性能優化總結

  咱們知道JAVA語言與C語言的其中一個區別就是JVM中有垃圾回收器能夠經過對運行中的對象進行判斷是否存活而且將在內存中已經不在使用的對象進行回收釋放其所佔用的內存,而C語言須要進行手動的釋放內存,1個對象的建立使用釋放都須要程序進行顯式的操做。固然不論是C仍是JAVA都有本身適合的開發領域。
  對於代碼性能優化,對於項目前期因爲前期數據量並非太大可是隨着時間的推移數據量的激增,若是沒有良好的編碼習慣後期會帶來較大的性能開銷,故項目初期編碼過程應養成良好的習慣避免後期繁重的codereview等。
  本篇博客將筆者在工做過程當中遇到的性能瓶頸以及在其中是如何進行優化進行記錄,以便後續有相關需求的小夥伴進行採納借鑑。
***java

對象篇

  在JMM中(JAVA內存模型)中保存對象的開銷實際上是至關大的,而且JMM要求對象必須按照8個字節對齊,雖然JMM會提供對字段的重排序來避免用戶隨意指定對象字段的順序以此來嘗試各個字段重排達到下降總體的對象開銷。可是即便是這樣1個空的String對象仍然得佔用 對象頭(8 字節)+ 引用 (4 字節 ) + char 數組(16 字節)+ 1個 int(4字節)+ 1個long(8字節)= 40 字節 若是在程序過程當中產生大量的對象無疑會加劇GC的工做。所以在實際編碼過程當中應該避免建立大量的對象,在不影響代碼可讀性及程序併發問題時,應該儘量複用已有的對象,減輕GC的壓力。正則表達式

  1. 使用StringBuild/StringBuffer代替循環體內進行字符串拼接,StringBuild與StringBuffer底層會默認建立一個16char數組進行字符串緩存,等到須要時在建立出String對象,而不是每一次都進行String對象建立。
  2. 使用StringTokenizer 代替Split 作字符串切割(若是不涉及到複雜正則只是簡單字符串切割)
  3. JAVA編譯時會對常量拼接進行從新定義做爲一個完整的常量故無需糾結性能問題,可見以下程序塊。
final String aa = "a"
final String bb = "b"
System.err.println((aa + bb) == (aa + bb));//true
  1. JDK1.6先後的subString方法實現
      在JDK1.6 subString內部實現是子String仍然會保留父String的char數組引用,這在必定程度上會形成內存泄露,特別是當有特別長的字符串實際上只須要其特別短的字符串,可是因爲引用依賴,GC沒法回收,形成父String一直滯留在JVM內存中沒法回收。
      JDK1.6以後subString內部對其優化,調用拷貝父String的char數組中的子char數組造成一份新的副本,如此子String與父String之間不存在依賴關係,GC可以對不在使用的父String進行GC回收。
  2. 循環遍歷中若是存在重複建立大量相同的字符串,建議建立緩存池進行對象緩存。
  3. 避免使用正則表達式,如必要使用至少要把Pattern進行緩存,避免反正建立Pattern編譯。
  4. 當須要對一個基本數據類型進行字符串轉換應該儘量使用toString或者String.valueOf(obj) 替換 obj+""。
  5. 在進行大文本字符串拼接時,應該爲StringBuffer,StringBuilder設置初始化容量值
    ***數據庫

    JVM篇

  6. -Xms-Xmx設置相同,避免每次垃圾回收完成後JVM從新分配內存。
  7. 原則上應該避免太大深度的遞歸,畢竟遞歸越深其中間棧幀所產生的數據引用仍然有效,沒法被GC清除。
  8. 當虛擬機棧中須要存儲基本數據或對象引用時須要調整-Xss來避免發生StackOverflowError異常
  9. 在虛擬機棧中隨着棧幀的pop會對棧幀內的內存進行及時的清理,因此在局部方法內部中其中間變量應該儘量使用8大基本數據類型纔可以隨着棧幀結束而當即內存回收。
  10. 爲了不頻繁觸發JVM對基本數據類型進行拆包與裝包操做,其中間變量應該儘可能使用基本數據類型而不是其包裝類型。
  11. JDK默認只緩存-128~+127的Integer和Long 若是超出該範圍則會建立出新的對象,若是對計算數據敏感但是適當經過-XX:AutoBoxCacheMax增大緩存範圍。
  12. G1與CMS收集器的選擇,G1收集器會對堆內內存進行劃分紅Region,其堆越小則劃分的Region數也會越少,在小堆的表現並不會比CMS突出。筆者認爲8G如下采用CMS比較合適。目前比較期待Java 11 新加入的ZGC號稱能夠達到10ms 如下的 GC 停頓。
  13. 經過-XX:+AlwaysPreTouch提早初始化好真正的物理內存,而不是須要才進行內存申請初始化。默認未添加該參數時而-Xms、-Xmx只是告訴告訴操做系統須要多少內存,從而避免被其餘進程使用,而只有當正直使用時纔會進行內存逐漸申請。好比在堆中Eden區進行對象建立又或者Young區轉Old區的內存空間。不過該參數也會影響啓動時長,隨着堆內存越大啓動時間也會增大。
  14. JDK監控工具 Jconsole,jProfile,VisualVM 能夠經過可視化界面查看JAVA堆內存使用狀況進行判斷是否須要進行代碼優化。
    ***數組

    線程篇

  15. 如何爲線程池選擇合適線程數?
      線程任務通常能夠分爲計算密集型IO密集型
      計算密集型CPU處於忙碌中,此時須要作內存數據讀寫計算,沒有任務的阻塞狀態。而IO密集型任務,在執行IO時堵塞,CPU處於等待狀態,等待過程當中系統會將時間片分給其餘線程處理。
      計算密集型任務線程數最好與系統核心數掛鉤,畢竟4核單線程主機在某一時刻只能同時跑4個線程,若是過多的線程數反而會由於切換上下文而耗費更多的任務時間,能夠經過調用JDK自帶的方法Runtime.availableProcessors得到系統支持的能夠核心數。N+1。
      對於IO密集型,合適的線程數能夠得到良好的性能支持,系統經過將IO阻塞的任務線程的時間片交給未被阻塞的任務線程,經過合適的調度發揮出比單線程更好的性能支持。在選擇線程數應該考慮內存支持程度,避免過多的線程數致使內存激增產生OOM。2N+1。
      線程等待時間所佔比例越高,須要越多線程。線程CPU時間所佔比例越高,須要越少線程。
      一個完整的系統不該只有一個線程池,應該對線程任務進行梳理分類,劃分出各自的任務類型以及工做負載來提供多個線程池。
  16. 若是須要加鎖竟可能使用加鎖代碼塊將鎖範圍控制在最小範圍中,而不是在方法體中加鎖。
  17. 儘可能避免嵌套取鎖,容易形成死鎖問題。
  18. 合理時候使用讀寫鎖來替換synchronized獨佔鎖。
  19. JDK1.8流處理對於大量須要計算的數據時可採用parallelStream進行併發數據處理,可提升處理速度。
  20. 多瞭解各種對象對併發支持性例如HashMap、SimpleDateFormat等。
  21. 合理使用ThreadLocal來實現數據在線程本地化。
  22. 若是對執行過程沒有嚴格的串行順序能夠採用FutureTask對計算結果統一取值拼裝。
    ***緩存

    雜談篇

  23. 爲避免循環之間切換,儘可能採用小循環嵌套大循環。
  24. 避免在循環中大機率拋出異常的代碼塊進行TryCatch,儘量放在循環外層TryCatch。
  25. 代碼塊中應儘可能使用懶加載,即須要時才進行加載,不須要則不加載。
  26. 在JVM級別作適當的緩存級別,可使用EhCache、Guava Cache。Ehcache適合支持持久化功能,有集羣解決方案,而Guava Cache只是一個支持LRU的concurrentHashMap,沒有Ehcache那麼多特性,只支持增刪改查,刷新規則和時效規則設定等最基本的設定。
  27. 設置日誌輸出級別,避免大量可有可無的日誌輸出,影響業務系統性能。
  28. SQL調優能夠經過索引分析,減小查詢字段,限制表讀數據等進行分析調優。
  29. 數據庫瓶頸能夠經過讀寫分離減小單服務器壓力,以及經過垂直拆分或水平拆分將數據表數據合理治理。
  30. 引入緩存架構減輕數據庫壓力。性能優化

    哪些是性能瓶頸的關鍵點

  31. 有些任務須要大量的計算,須要不停地佔用CPU資源,致使其餘任務搶佔CPU的能力變弱而致使響應速度降低,而帶來的性能問題。好比任務內過多的重複計算,無限的自循環計算,JVM頻繁的FULL GC,多線程作大量的上下文切換等。
  32. 通常來講說內存的讀寫速度很是快,通常不存在性能問題,可是因爲內存成本比硬盤高即內存空間是有限的,應該注意內存的範圍避免應內存耗盡而致使OOM等問題。
  33. 磁盤IO是引發系統性能的一大因素。涉及到數據落地等問題應儘可能使用硬盤順序讀寫而不是隨機讀寫,順序讀寫的讀寫能力比隨機讀寫能力強大太多。
  34. 數據庫方面除了必要的SQL優化減小查詢時間外若有必要須要引入緩存層減輕數據庫壓力
  35. 合理使用鎖減小併發時形成性能損耗
  36. 防止瞬間時拋出大量的異常,拋出異常會不停從堆棧內拔取異常信息,很是耗性能。服務器

    衡量性能的指標

  37. 系統響應時間
      從發送請求到接收到數據時所須要的時間
  38. 系統吞吐量
      單位時間內成功地傳送數據的數量大小
  39. 負載承受能力
      隨着併發量的增長最終致使系統拋出大量的異常,整個系統處於不可用的時候。多線程

相關文章
相關標籤/搜索