多核平台下的JAVA最佳化

來源:互聯網
上載者:User

現在多核CPU是主流。利用多核技術,可以有效發揮硬體的能力,提升輸送量,對於Java程式,可以實現並發垃圾收集。但是Java利用多核技術也帶來了一些問題,主要是多線程共用記憶體引起了。目前記憶體和CPU之間的頻寬是一個主要瓶頸,每個核可以獨享一部分快取,可以提高效能。JVM是利用作業系統的"輕量級進程"實現線程,所以線程每操作一次共用記憶體,都無法在快取中命中,是一次開銷較大的系統調用。所以區別於普通的最佳化,針對多核平台,需要進行一些特殊的最佳化。

代碼最佳化

線程數要大於等於核心數

如果使用多線程,只有啟動並執行線程數比核心數大,才有可能榨乾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來啟動配置。

效能可以看這個評測,效能的提升是很可觀。



啟用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越大,對象可以在年輕態呆更多時間。

等等

 

參見:《Java最佳化白皮書》,《深入理解Java虛擬機器》

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.