1. 程式人生 > >Java的大記憶體分頁支援

Java的大記憶體分頁支援

引用 :http://kilik.iteye.com/blog/677253

最近在研究java的效能調優,順手寫了一個小程式來測試效能問題。這個程式用來進行矩陣乘法運算,如下:

Java程式碼  收藏程式碼
  1. for (int i = 0; i < 2048; i++)  
  2.     for (int j = 0; j < 2048; j++)  
  3.         for (int k = 0; k < 2048; k++)  
  4.             res[i][j] += mul1[i][k] * mul2[k][j];  

在ubuntu 10.04(64bit)下,JDK 1.6.0.20執行該程式共耗時76秒。分析下來,影響執行速度的因素主要有兩個:cache miss  和 TLB miss.

這裡主要講TLB miss的問題,cache miss留待下回分解。由於,在預設情況下記憶體分頁大小為4K, 而每次作乘法時,取值均跨至少8K(4*2048)的範圍,而一級資料頁表快取(L1 DTLB)是非常小的,Intel Core 2架構下4KB小頁表的條目只有16個。這意味著TLB miss的概率很高,最差情況下每次資料訪問都將出現一次miss;而使用大記憶體分頁(如,2M)後,大概每256次資料訪問出現一次miss。實際情況確實反映了這一現象,使用大記憶體頁後,同樣的程式耗時大幅下降到45秒。

接下來介紹如何在Ubuntu 10.04(64bit) + JDK (Hotspot 1.6.0.20) 環境下啟用大記憶體頁,並指定jvm使用大記憶體頁。這些步驟應該也可以應用到其他linux系統。(注,為完成下列步驟,使用者需要有root許可權)

1. 瞭解linux系統對大記憶體頁的支援。

# grep Huge /proc/meminfo

HugePages_Total: 0
HugePages_Free: 0
Hugepagesize: 2048 kB


說明,系統支援2M的大記憶體分頁。

2. 修改核心引數,為large page預留記憶體

     a. 設定共享記憶體段最大值,最少要大於jvm使用的large page的記憶體。

          如需要設定大小為2G(1024*1024*1024*2=2147483648),則新增下行到檔案 /etc/sysctl.conf

          kernel.shmmax=2147483648

     b. 設定需要預留多少大記憶體頁。

          如需要為jvm預留1G的large page記憶體,則需要預留512頁大記憶體頁(512*2M=1G)

          新增下行到檔案 /etc/sysctl.conf

          vm.nr_hugepages=512

3. 為你的程序新增訪問large page共享記憶體段的許可權

     新增新的使用者組,並把自己加入到這個組。如,新增使用者組 hugetlb,並把當前使用者 kilik 新增到該組。

     新增下行到檔案 /etc/sysctl.conf ,其中1001為使用者組hugetlb的gid。

     vm.hugetlb_shm_group = 1001

4. 修改使用者安全設定,允許程序鎖定更大的記憶體段

     large page共享記憶體必須鎖定到主存,不能swap到磁碟,因此需要修改使用者的memlock設定。新增如下兩行到檔案 /etc/security/limits.conf。其中,1048576代表1G(1024*1024 K)

     kilik    hard    memlock    1048576 
     kilik    soft    memlock    1048576

5. 重啟OS以使上述設定生效。

6. 新增相關jvm執行引數,告訴jvm使用large page記憶體。

    不同的jvm有不同的引數設定來開啟大記憶體頁的支援,對Sun Hotspot而言,這個引數是 -XX:+UseLargePages。因此可以使用如下命令列來執行矩陣乘法程式。

    java -XX:+UseLargePages -Xmx512m -Xms512m -cp . org.kilik.perf.ClassicMatrixMulti