Java的大記憶體分頁支援
引用 :http://kilik.iteye.com/blog/677253
最近在研究java的效能調優,順手寫了一個小程式來測試效能問題。這個程式用來進行矩陣乘法運算,如下:
Java程式碼- for (int i = 0; i < 2048; i++)
- for (int j = 0; j < 2048; j++)
- for (int k = 0; k < 2048; k++)
- 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