多核平臺下的JAVA優化

代碼優化java

  線程數要大於等於核數linux

  若是使用多線程,只有運行的線程數比核數大,纔有可能榨乾 CPU 資源,不然會有若干核閒置。要注意的是,若是線程數目太多,就會佔用過多內存,致使性能不升反降。JVM 的垃圾回收也是須要線程的,因此這裏的線程數包含 JVM 本身的線程算法

  儘可能減小共享數據寫操做數組

  每一個線程有本身的工做內存,在這個區域內,系統能夠毫無顧忌的優化,若是去讀共享內存區域,性能也不會降低。可是一旦線程想寫共享內存(使用 volatile 關鍵字),就會插入不少內存屏障操做(Memory Barrier 或者 Memory Fence)指令,保證處理器不亂序執行。相比寫本地線程自有的變量,性能降低不少。處理方法是儘可能減小共享數據,這樣也符合」數據耦合」的設計原則。緩存

  使用 synchronize 關鍵字多線程

  在 Java1.5 中,synchronize 是性能低效的。由於這是一個重量級操做,須要調用操做接口,致使有可能加鎖消耗的系統時間比加鎖之外的操做還多。相比之下使用 Java 提供的 Lock 對象,性能更高一些。可是到了 Java1.6,發生了變化。synchronize 在語義上很清晰,能夠進行不少優化,有適應自旋,鎖消除,鎖粗化,輕量級鎖,偏向鎖等等。致使在 Java1.6 上 synchronize 的性能並不比 Lock 差。官方也表示,他們也更支持 synchronize,在將來的版本中還有優化餘地。架構

  使用樂觀策略併發

  傳統的同步併發策略是悲觀的。表現語義爲:多線程操做一個對象的時候,總以爲會有兩個線程在同時操做,因此須要鎖起來。樂觀策略是,假設平時就 一個線程訪問,當出現了衝突的時候,再重試。這樣更高效一些。Java 的 AtomicInteger 就是使用了這個策略。性能

  使用線程本地變量(ThreadLocal)測試

  使用 ThreadLocal 能夠生成線程本地對象的副本,不會和其餘線程共享。當該線程終止的時候,其本地變量能夠所有回收。

  類中 Field 的排序

  能夠將一個類會頻繁訪問到的幾個 field 放在一塊兒,這樣他們就有更多的可能性被一塊兒加入高速緩存。同時最好把他們放在頭部。基本變量和引用變量不要交錯排放。

  批量處理數組

  如今處理器能夠用一條指令來處理一個數組中的多條記錄,例如能夠同時向一個 byte 數組中讀或者寫 store 記錄。因此要儘可能使用 System.arraycopy ()這樣的批量接口,而不是本身操做數組。

  JVM 優化

  啓用大內存頁

  如今一個操做系統默認頁是4K。若是你的 heap 是4GB,就意味着要執行1024*1024次分配操做。因此最好能把頁調大。這個配額設計操做系統,單改 Jvm 是不行的。Linux 上的配置有點複雜,不詳述。

  在 Java1.6 中 UseLargePages 是默認開啓的,LasrgePageSzieInBytes 被設置成了4M。筆者看到一些狀況下配置成了128MB,在官方的性能測試中更是配置到256MB。

  啓用壓縮指針

  Java 的64的性能比32慢,緣由是由於其指針由32位擴展到64位,雖然尋址空間從4GB 擴大到 256 TB,但致使性能的降低,並佔用了更多的內存。因此對指針進行壓縮。壓縮後的指針最多支持32GB 內存,而且能夠得到32位 JVM 的性能。

  在 JDK6 update 23 默認開啓了,以前的版本可使用-XX:+UseCompressedOops 來啓動配置。

  性能能夠看這個評測,性能的提高是很可觀。

多核平臺下的JAVA優化

  啓用 NUMA

  numa 是一個 CPU 的特性。SMP 架構下,CPU 的核是對稱,可是他們共享一條系統總線。因此 CPU 多了,總線就會成爲瓶頸。在 NUMA 架構下,若干 CPU 組成一個組,組之間有點對點的通信,相互獨立。啓動它能夠提升性能。

  NUMA 須要硬件,操做系統,JVM 同時啓用,才能啓用。Linux 能夠用 numactl 來配置 numa,JVM 經過-XX:+UseNUMA 來啓用。

  激進優化特性

  在 Java1.6 中,激進優化(AggressiveOpts)是默認開啓的。激進優化是通常有一些下一個版本纔會發佈的優化選項。可是有可能形成不穩定。前段時間以訛傳訛的 JDK7的 Bug,就是開啓這個選項後測到的。

  逃逸分析

  讓一個對象在一個方法內建立後,若是他傳遞出去,就能夠稱爲方法逃逸;若是傳遞到別的線程,成爲線程逃逸。若是能知道一個對象沒有逃逸,就能夠 把它分配在棧而不是堆上,節約 GC 的時間。同時能夠將這個對象拆散,直接使用其成員變量,有利於利用高速緩存。若是一個對象沒有線程逃逸,就能夠取消其中一切同步操做,很大的提升性能。

  可是逃逸分析是頗有難度的,由於花了 cpu 去對一個對象去分析,要是他不逃逸,就沒法優化,以前的分析血本無歸。因此不能使用複雜的算法,同時如今的 JVM 也沒有實現棧上分配。因此開啓以後,性能也可能降低。

  可使用-XX:+DoEscapeAnalysis 來開啓逃逸分析。

  高吞吐量 GC 配置

  對於高吞吐量,在年輕態可使用 Parallel Scavenge,年老態可使用 Parallel Old 垃圾收集器。

  使用-XX:+UseParallelOldGC 開啓

  能夠將-XX:ParallelGCThreads 根據 CPU 的個數進行調整。能夠是 CPU 數的1/2或者5/8

  低延遲 GC 配置

  對於低延遲的應用,在年輕態可使用 ParNew,年老態可使用 CMS 垃圾收集器。

  可使用-XX:+UseConcMarkSweepGC 和-XX:+UseParNewGC 打開。

  能夠將-XX:ParallelGCThreads 根據 CPU 的個數進行調整。能夠是 CPU 數的1/2或者5/8

  能夠調整-XX:MaxTenuringThreshold (晉升年老代年齡)調高,默認是15.這樣能夠減小年老代 GC 的壓力

  能夠-XX:TargetSurvivorRatio,調整 Survivor 的佔用比率。默認50%.調高能夠提供 Survivor 區的利用率

  能夠調整-XX:SurvivorRatio,調整 Eden 和 Survivor 的比重。默認是8。這個比重越小,Survivor 越大,對象能夠在年輕態呆更多時間。

相關文章
相關標籤/搜索