多核平臺下的Java優化
現在多核 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 來啟動配置。
性能可以看這個評測,性能的提升是很可觀。
posted on 2011-11-15 14:34 順其自然EVO 閱讀(173) 評論(0) 編輯 收藏 所屬分類: 測試學習專欄