簡單-高效-優雅

          [轉帖]如何在IBM JDK 1.4.2的環境中避免Java堆空間的碎片問題

          用戶在使用WebSphere Application Server(以下簡稱WAS)運行自己應用的時候經常會碰到Out Of Memory的問題(簡稱OOM問題),其中很大一部分的情況是Java堆空間碎片問題引起的OOM問題。IBM JDK 1.4.2的版本中JDK對GC的行為做出了一定的改進。其中一些JDK參數的引進可以改善Java堆空間的碎片問題。

          本文首先會給出IBM JDK 1.4.2中對于K簇(k-cluster)和P簇(p-cluster)工作模式的解釋。然后在此基礎上介紹JDK 1.4.2為解決碎片問題采取的新算法。最后,給出WAS中為改善Java堆空間碎片問題使用的JDK運行參數。

          一、K簇和P簇
          在 Java堆空間中分配的內存對象通常是可以移動,如果垃圾回收程序(garbage collector)決定重新序列化堆空間的時候,可以四處移動這些對象。然而,有些對象永遠或者臨時無法移動。這些固定不動的對象就是常說的pin對象 (pinned object)。
          在IBM JDK 1.4.2中,垃圾回收程序首先會分配一個K簇作為堆空間底部的第一個對象。K簇是專門用來存儲“類塊”(class block)的區域。K簇可以容納1280個類塊條目。每個類塊的大小是256個字節。緊接著垃圾回收程序會分配一個P簇作為堆空間中的第2個對象。P簇 是用來存儲pin對象的區域。第一個P簇的默認大小為16KB。

          當K簇滿了的情況下,垃圾回收程序在P簇中繼續分配類塊。當P簇滿了的情況下,垃圾回收程序會分配一個大小為2KB的新P簇。由于這些新的P簇可以被分配到任何地方而且又不能被移動,這就造成了碎片的問題。

          二、pinnedFreeList算法
          為了解決這些問題,IBM JDK 1.4.2版本中起用了pinnedFreeList來改變P簇的分配方法。方法的關鍵是在每一次GC(garbage collection)后,垃圾回收程序從未分配列表的底部分配一些存儲區并把它們串到pinnedFreeList上。分配P簇的請求將從 pinnedFreeList分配空間,而其他分配內存的請求將從堆的未分配列表上分配。無論堆的未分配列表或者pinnedFreeList被耗盡,垃 圾回收程序都會造成一次分配失敗并且引起GC。這種方法確保所有的P簇被分配在堆空間盡可能低的位置。

          垃圾回收程序按照如下的算法確定給pinnedFreeList分配多少存儲空間:
          ● 初始分配的空間是50KB
          ● 如果不是初始分配并且pinnedFreeList為空,那么垃圾回收程序會比較50KB和從上一次GC到現在總共分配P簇大小5倍的數值,按照較大的數值分配
          ● 如果不是初始分配并且pinnedFreeList不為空,那么垃圾回收程序會比較P簇溢出設定值(默認為2K)和從上一次GC到現在總共分配P簇大小5倍的數值,按照較大的數值分配。

          這一算法在應用需要加載很多類的情況下會增大pinnedFreeList的大小。這樣可以避免由于pinnedFreeList耗盡引起的分配失敗。同 時算法在分配很少P簇的情況下會減少pinnedFreeList的大小。這樣可以避免pinnedFreeList占用過多的堆空間。
          buildPinnedFreeList函數利用上面的算法構建pinnedFreeList。這個函數在如下地方會被調用:
          ● 在初始化簇(initializeClusters)時
          ● 在堆空間擴展(expandHeap)結束時
          ● 在gc0_locked結束時

          垃圾回收程序通過調用nextPinnedCluster函數在pinnedFreeList中分配P簇。這個函數的工作方式類似于nextTLH工作方 式:總是從pinnedFreeList獲取下一個空的塊。如果pinnedFreeList空了,會產生manageAllocFailure。
          在realObjCAlloc里,如果在P簇中沒有空間了,垃圾回收程序就會調用nextPinnedCluster函數分配一個新的P簇。
          在初始化簇(initializeClusters)時,垃圾回收程序調用nextPinnedCluster,nextPinnedCluster會分 配一個50K大小的初始P簇,因為pinnedFreeList中唯一的空余塊的大小是50K。空余塊的大小等于50K是因為 pinnedFreeList在初始狀態下被設置為50K。

          三、調整Java運行參數
          對于一個大的Java應用,比如:WAS,默認的K簇可能不足以分配所有的類塊。在IBM JDK 1.4.2版本中,可以通過使用-Xk和-Xp命令行參數來設定K簇和P簇的大小,例如:-Xknnnn (其中nnnn代表K簇中可以容納的類塊的最大數目。)

          通過添加Java的運行是參數-Dibm.dg.trc.print=st_verify 可以在GC的詳細信息中得到合適nnnn的值,例如:
          <GC(VFY-SUM): pinned=4265(classes=3955/freeclasses=0) dosed=10388 movable=1233792 free=5658>
          pinned和classes的數值可以為-Xk的正確數值提供參考。一般推薦使用classes(3955)數值的110%左右,所以在這個例子中-Xk4200是一個合適的設置。

          盡管,pinned和classes的數值之間的差值給pCluster的初始大小提供了線索。但是,因為每一個對象可能有不同的大小,所以很難預測P簇所需要的大小和P簇溢出的大小。

          用 戶可以通過-Xp命令行參數-Xp設定P簇的初始大小和溢出大小。例如:-Xpiiii[K][,oooo[K]] (其中,iiii代表P簇的初始大小,單位是KB,oooo是可選的,代表溢出P簇(后續的P簇)的大小。iiii和oooo的默認值為16KB和 2KB。)

          如果用戶的應用確實遇到了堆空間碎片的問題,可以考慮打開GC的詳細信息并使用-Dibm.dg.trc.print=st_verify參數,并從分析值中得到合適的-Xk值。如果問題依舊存在,可以考慮試驗加大P簇的初始大小和溢出大小。

          posted on 2008-04-24 17:51 BigOnion 閱讀(321) 評論(0)  編輯  收藏 所屬分類: 性能相關

          主站蜘蛛池模板: 蕉岭县| 吉水县| 长武县| 云安县| 南靖县| 曲松县| 慈溪市| 永州市| 普格县| 清徐县| 南昌市| 马鞍山市| 临澧县| 广河县| 瑞丽市| 临夏市| 松潘县| 礼泉县| 小金县| 永康市| 潞城市| 恭城| 斗六市| 麻栗坡县| 英德市| 安吉县| 青神县| 龙州县| 阿拉善右旗| 谷城县| 裕民县| 平度市| 陆川县| 益阳市| 马尔康县| 饶河县| 汉寿县| 开平市| 芒康县| 西畴县| 清苑县|