??xml version="1.0" encoding="utf-8" standalone="yes"?>
一、K和P?br />
?
Java堆空间中分配的内存对象通常是可以移动,如果垃圾回收E序Qgarbage
collectorQ决定重新序列化堆空间的时候,可以四处Udq些对象。然而,有些对象永远或者时无法移动。这些固定不动的对象是常说的pin对象
Qpinned objectQ?br />
在IBM JDK
1.4.2中,垃圾回收E序首先会分配一个K作为堆I间底部的第一个对象。K是专门用来存储“cd”Qclass
blockQ的区域。K可以容U?280个类块条目。每个类块的大小?56个字节。紧接着垃圾回收E序会分配一个P作为堆I间中的W?个对象。P?
是用来存储pin对象的区域。第一个P的默认大小?6KB?br />
当K满了的情况下,垃圾回收E序在P中l箋分配cd。当P满了的情况下,垃圾回收E序会分配一个大ؓ2KB的新P。由于这些新的P可以被分配CQ何地方而且又不能被UdQ这造成了碎片的问题?/p>
二、pinnedFreeList法
Z解决q些问题QIBM JDK
1.4.2版本中v用了pinnedFreeList来改变P的分配Ҏ。方法的关键是在每一ơGCQgarbage
collectionQ后Q垃圑֛收程序从未分配列表的底部分配一些存储区q把它们串到pinnedFreeList上。分配P的h从
pinnedFreeList分配I间Q而其他分配内存的h从堆的未分配列表上分配。无论堆的未分配列表或者pinnedFreeList被耗尽Q垃
圑֛收程序都会造成一ơ分配失败ƈ且引起GC。这U方法确保所有的P被分配在堆I间可能低的位|?/p>
垃圾回收E序按照如下的算法确定给pinnedFreeList分配多少存储I间Q?br />
?初始分配的空间是50KB
?如果不是初始分配q且pinnedFreeList为空Q那么垃圑֛收程序会比较50KB和从上一ơGC到现在d分配P大?倍的数|按照较大的数值分?br />
?如果不是初始分配q且pinnedFreeList不ؓI,那么垃圾回收E序会比较P溢定|默认?KQ和从上一ơGC到现在d分配P大?倍的数|按照较大的数值分配?br />
q一法在应用需要加载很多类的情况下会增大pinnedFreeList的大。这样可以避免由于pinnedFreeList耗尽引v的分配失败。同
时算法在分配很少P的情况下会减少pinnedFreeList的大。这样可以避免pinnedFreeList占用q多的堆I间?br />
buildPinnedFreeList函数利用上面的算法构建pinnedFreeList。这个函数在如下地方会被调用Q?br />
?在初始化(initializeClustersQ时
?在堆I间扩展QexpandHeapQ结束时
?在gc0_lockedl束?br />
垃圾回收E序通过调用nextPinnedCluster函数在pinnedFreeList中分配P。这个函数的工作方式cM于nextTLH工作?
式:L从pinnedFreeList获取下一个空的块。如果pinnedFreeListIZQ会产生manageAllocFailure?br />
在realObjCAlloc里,如果在P中没有I间了,垃圾回收E序׃调用nextPinnedCluster函数分配一个新的P?br />
在初始化(initializeClustersQ时Q垃圑֛收程序调用nextPinnedClusterQnextPinnedCluster会分
配一?0K大小的初始P,因ؓpinnedFreeList中唯一的空余块的大是50K。空余块的大等?0K是因?
pinnedFreeList在初始状态下被设|ؓ50K?/p>
三、调整Javaq行参数
对于一个大的Java应用Q比如:WASQ默认的K可能不以分配所有的cd。在IBM JDK 1.4.2版本中,可以通过使用-Xk?Xp命o行参数来讑֮K和P的大小Q例如:-Xknnnn (其中nnnn代表K中可以容纳的类块的最大数目?
通过dJava的运行是参数-Dibm.dg.trc.print=st_verify 可以在GC的详l信息中得到合适nnnn的|例如Q?br />
<GC(VFY-SUM): pinned=4265(classes=3955/freeclasses=0) dosed=10388 movable=1233792 free=5658>
pinned和classes的数值可以ؓ-Xk的正数值提供参考。一般推荐用classesQ?955Q数值的110%左右Q所以在q个例子?Xk4200是一个合适的讄?/p>
管Qpinned和classes的数g间的差值给pCluster的初始大提供了U烦。但是,因ؓ每一个对象可能有不同的大,所以很NP所需要的大小和P溢出的大小?/p>
? 户可以通过-Xp命o行参?Xp讑֮P的初始大小和溢出大。例如:-Xpiiii[K][,oooo[K]] (其中Qiiii代表P的初始大小Q单位是KBQoooo是可选的Q代表溢出P(后箋的P)的大。iiii和oooo的默认gؓ16KB? 2KB?
如果用户的应用确实遇C堆空间碎片的问题Q可以考虑打开GC的详l信息ƈ使用-Dibm.dg.trc.print=st_verify参数Qƈ从分析g得到合适的-Xk倹{如果问题依旧存在,可以考虑试验加大P的初始大小和溢出大?/p>
当用户在使用WAS的时候碰C分配大对象引L片问题。找到造成分配p|的线E堆栈信息对于解决问题将会是非常有帮助的。本文提供的工具可以帮助用户Q?br /> ?打印分配p|的线E堆栈信?br /> ?通过讄分配p|的内存大,限制堆栈信息的打?br /> ?通过讄Ҏ调用的深度,限制堆栈信息的打?br /> 需要注意的是,q个工具在某些情况下可能无法报告堆栈中方法的准确名字。在q种情况下,用户需要通过其他机制扑ֈ大对象的分配Ҏ或者联pL地的IBM技术支持?/p>
Z使用本工P用户首先需要按照下面的步骤配置java虚拟机,使其能够加蝲所需的类库:
1Q从下面的连接中扑ֈq下载Profiler.zip。展开q个压羃文gQ可以看到Profiler.zip包含多个压羃文g。找到对应操作系l的压羃
文gQ比如:swprof_Windows.zip对应Windows操作pȝQ展开q得到对应的cd文gQ比如:libswprof.so或?
liballocprof.so或者swprofiler.dll。拷贝类库文件到<WAS_Home>"java"jre"bin路径下?br />
http://www-1.ibm.com/support/docview.wss?uid=swg21162314&aid=1
其中<WAS_Home>代表WAS的安装目录?br />
注意Q这些类库文仉Ҏ个操作系l是不同的,保扑ֈ与操作系l对应的cd文gq拷贝到指定的\径下?br />
2Q对于AIX、Linux、Linux on
zSeries、Windowsq_上的WASQ在Java虚拟机的命o行参CdQ?Xrunswprof。对于Solarisq_上的WASQ在
Java虚拟机的命o行参CdQ?Xrunallocprof。添加的具体办法请参考WAS的信息中心:
http://www-306.ibm.com/software/webservers/appserv/was/library/
3Q重新启动应用服务器
4Q如果,cd被成功的加蝲Q那么在应用服务器启动过E的stderr日志中(比如Qnative_stderr.logQ可以看到类似如下的信息Q?br />
swprofiler loaded OK
Allocation limit: XXXX, Depth: YYY
Z更好的利用该工具Q用户可以定制打印堆栈的分配大小和深度。其中分配大通过讑֮环境变量ALLOC_LIMIT来定Ӟ分配深度通过讑֮变量
ALLOC_DEPTH来定制。每当应用程序尝试分配超q分配限制大的对象Ӟ工具׃在应用服务器的stderr日志中(?
如:native_stderr.logQ打印申误个对象的Ҏ信息。分配深度控制着堆栈被打印的层数。两者的默认值是50000?0。这意味着Q?
默认情况下当某个应用试分配过50000个字节大的对象Ӟ工具打印堆栈的?0层方法。例如:
……
<AF[591]: Allocation Failure. need 22616440 bytes, 6476 ms since last
AF>
<AF[591]: managing allocation failure, action=2 (118062832/674101760)>
<GC(591): GC cycle started Fri Dec 09 08:19:50 2005
<GC(591): freed 481241408 bytes, 88% free (599304240/674101760), in
105 ms>
<GC(591): mark: 90 ms, sweep: 15 ms, compact: 0 ms>
<GC(591): refs: soft 3 (age >= 32), weak 0, final 21, phantom 1>
<AF[591]: completed in 106 ms>
Large object allocated: size 22616428
at testalloc in class com/test/OOMTest 21616424
at service in class javax/servlet/http/HttpServlet
……
用户可以按照如下的方法设定这2个环境变量:
WAS V6.0Q?br />
1Q?登陆WAS理控制Cơ选择Q?br />
服务?> 应用服务?> 服务器名Uͼ比如Qserver1Q?gt; Java和进E管?> q程定义 > 环境条目 > 新徏
2Q?d如下的环境变量和|
名称 ?br />
ALLOC_LIMIT 限定的字节数Q比如:600000Q?br />
ALLOC_DEPTH 限定的堆栈深度(比如Q?0Q?br />
3Q?保存讄重新启动应用服务?/p>
WAS V5.0 & 5.1Q?br />
1Q?登陆WAS理控制Cơ选择Q?br />
服务?> 应用服务?> 服务器名Uͼ比如Qserver1Q?gt; q程定义 > 环境条目 > 新徏
2Q?d如下的环境变量和|
名称 ?br />
ALLOC_LIMIT 限定的字节数Q比如:600000Q?br />
ALLOC_DEPTH 限定的堆栈深度(比如Q?0Q?br />
3Q?保存讄重新启动应用服务?/p>
Unix/Linuxq_上的WAS V4.0 & 4.1Q?br />
1Q?dcM如下?行在startupServer.sh脚本文g的开_q个文g位于<WAS_Home>/bin路径下:
export ALLOC_LIMIT=600000
export ALLOC_DEPTH=10
2Q?重新启动应用服务?/p>
Windowsq_上的WAS V4.0 & 4.1Q?br />
1Q添加类似如下的2行在adminserver.bat脚本文g的开_q个文g位于<WAS_Home>/bin路径下:
SET ALLOC_LIMIT=600000
SET ALLOC_DEPTH=10
2Q?重新启动应用服务?/p>