﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-简单-高效-优雅-文章分类-性能相关</title><link>http://www.blogjava.net/spec-second/category/31006.html</link><description /><language>zh-cn</language><lastBuildDate>Thu, 24 Apr 2008 12:26:30 GMT</lastBuildDate><pubDate>Thu, 24 Apr 2008 12:26:30 GMT</pubDate><ttl>60</ttl><item><title>[转帖]如何在IBM JDK 1.4.2的环境中避免Java堆空间的碎片问题</title><link>http://www.blogjava.net/spec-second/articles/195665.html</link><dc:creator>BigOnion</dc:creator><author>BigOnion</author><pubDate>Thu, 24 Apr 2008 09:51:00 GMT</pubDate><guid>http://www.blogjava.net/spec-second/articles/195665.html</guid><wfw:comment>http://www.blogjava.net/spec-second/comments/195665.html</wfw:comment><comments>http://www.blogjava.net/spec-second/articles/195665.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/spec-second/comments/commentRss/195665.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/spec-second/services/trackbacks/195665.html</trackback:ping><description><![CDATA[用户在使用WebSphere Application
Server(以下简称WAS)运行自己应用的时候经常会碰到Out Of
Memory的问题（简称OOM问题），其中很大一部分的情况是Java堆空间碎片问题引起的OOM问题。IBM JDK
1.4.2的版本中JDK对GC的行为做出了一定的改进。其中一些JDK参数的引进可以改善Java堆空间的碎片问题。
<p> 本文首先会给出IBM JDK 1.4.2中对于K簇(k-cluster)和P簇(p-cluster)工作模式的解释。然后在此基础上介绍JDK 1.4.2为解决碎片问题采取的新算法。最后，给出WAS中为改善Java堆空间碎片问题使用的JDK运行参数。<br />
</p>
<p>一、K簇和P簇<br />
在
Java堆空间中分配的内存对象通常是可以移动，如果垃圾回收程序（garbage
collector）决定重新序列化堆空间的时候，可以四处移动这些对象。然而，有些对象永远或者临时无法移动。这些固定不动的对象就是常说的pin对象
（pinned object）。<br />
在IBM JDK
1.4.2中，垃圾回收程序首先会分配一个K簇作为堆空间底部的第一个对象。K簇是专门用来存储&#8220;类块&#8221;（class
block）的区域。K簇可以容纳1280个类块条目。每个类块的大小是256个字节。紧接着垃圾回收程序会分配一个P簇作为堆空间中的第2个对象。P簇
是用来存储pin对象的区域。第一个P簇的默认大小为16KB。<br />
<br />
当K簇满了的情况下，垃圾回收程序在P簇中继续分配类块。当P簇满了的情况下，垃圾回收程序会分配一个大小为2KB的新P簇。由于这些新的P簇可以被分配到任何地方而且又不能被移动，这就造成了碎片的问题。</p>
<p>二、pinnedFreeList算法<br />
为了解决这些问题，IBM JDK
1.4.2版本中起用了pinnedFreeList来改变P簇的分配方法。方法的关键是在每一次GC（garbage
collection）后，垃圾回收程序从未分配列表的底部分配一些存储区并把它们串到pinnedFreeList上。分配P簇的请求将从
pinnedFreeList分配空间，而其他分配内存的请求将从堆的未分配列表上分配。无论堆的未分配列表或者pinnedFreeList被耗尽，垃
圾回收程序都会造成一次分配失败并且引起GC。这种方法确保所有的P簇被分配在堆空间尽可能低的位置。</p>
<p> 垃圾回收程序按照如下的算法确定给pinnedFreeList分配多少存储空间：<br />
● 初始分配的空间是50KB<br />
● 如果不是初始分配并且pinnedFreeList为空，那么垃圾回收程序会比较50KB和从上一次GC到现在总共分配P簇大小5倍的数值，按照较大的数值分配<br />
● 如果不是初始分配并且pinnedFreeList不为空，那么垃圾回收程序会比较P簇溢出设定值（默认为2K）和从上一次GC到现在总共分配P簇大小5倍的数值，按照较大的数值分配。<br />
<br />
这一算法在应用需要加载很多类的情况下会增大pinnedFreeList的大小。这样可以避免由于pinnedFreeList耗尽引起的分配失败。同
时算法在分配很少P簇的情况下会减少pinnedFreeList的大小。这样可以避免pinnedFreeList占用过多的堆空间。<br />
buildPinnedFreeList函数利用上面的算法构建pinnedFreeList。这个函数在如下地方会被调用：<br />
● 在初始化簇（initializeClusters）时<br />
● 在堆空间扩展（expandHeap）结束时 <br />
● 在gc0_locked结束时<br />
<br />
垃圾回收程序通过调用nextPinnedCluster函数在pinnedFreeList中分配P簇。这个函数的工作方式类似于nextTLH工作方
式：总是从pinnedFreeList获取下一个空的块。如果pinnedFreeList空了，会产生manageAllocFailure。<br />
在realObjCAlloc里，如果在P簇中没有空间了，垃圾回收程序就会调用nextPinnedCluster函数分配一个新的P簇。<br />
在初始化簇（initializeClusters）时，垃圾回收程序调用nextPinnedCluster，nextPinnedCluster会分
配一个50K大小的初始P簇，因为pinnedFreeList中唯一的空余块的大小是50K。空余块的大小等于50K是因为
pinnedFreeList在初始状态下被设置为50K。</p>
<p>三、调整Java运行参数<br />
对于一个大的Java应用，比如：WAS，默认的K簇可能不足以分配所有的类块。在IBM JDK 1.4.2版本中，可以通过使用-Xk和-Xp命令行参数来设定K簇和P簇的大小，例如：-Xknnnn (其中nnnn代表K簇中可以容纳的类块的最大数目。)</p>
<p>通过添加Java的运行是参数-Dibm.dg.trc.print=st_verify 可以在GC的详细信息中得到合适nnnn的值，例如：<br />
&lt;GC(VFY-SUM): pinned=4265(classes=3955/freeclasses=0) dosed=10388 movable=1233792 free=5658&gt;<br />
pinned和classes的数值可以为-Xk的正确数值提供参考。一般推荐使用classes（3955）数值的110%左右，所以在这个例子中-Xk4200是一个合适的设置。</p>
<p>尽管，pinned和classes的数值之间的差值给pCluster的初始大小提供了线索。但是，因为每一个对象可能有不同的大小，所以很难预测P簇所需要的大小和P簇溢出的大小。</p>
<p>用
户可以通过-Xp命令行参数-Xp设定P簇的初始大小和溢出大小。例如：-Xpiiii[K][,oooo[K]]
(其中，iiii代表P簇的初始大小，单位是KB，oooo是可选的，代表溢出P簇（后续的P簇）的大小。iiii和oooo的默认值为16KB和
2KB。)</p>
<p> 如果用户的应用确实遇到了堆空间碎片的问题，可以考虑打开GC的详细信息并使用-Dibm.dg.trc.print=st_verify参数，并从分析值中得到合适的-Xk值。如果问题依旧存在，可以考虑试验加大P簇的初始大小和溢出大小。</p>
<img src ="http://www.blogjava.net/spec-second/aggbug/195665.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/spec-second/" target="_blank">BigOnion</a> 2008-04-24 17:51 <a href="http://www.blogjava.net/spec-second/articles/195665.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转帖]定位应用程序中对大对象的申请</title><link>http://www.blogjava.net/spec-second/articles/195664.html</link><dc:creator>BigOnion</dc:creator><author>BigOnion</author><pubDate>Thu, 24 Apr 2008 09:50:00 GMT</pubDate><guid>http://www.blogjava.net/spec-second/articles/195664.html</guid><wfw:comment>http://www.blogjava.net/spec-second/comments/195664.html</wfw:comment><comments>http://www.blogjava.net/spec-second/articles/195664.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/spec-second/comments/commentRss/195664.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/spec-second/services/trackbacks/195664.html</trackback:ping><description><![CDATA[用户在使用WebSphere Application
Server(以下简称WAS)运行自己应用的时候经常会碰到Out Of
Memory的问题（简称OOM问题）。其中很大一部分的情况是应用程序分配大对象造成的Java堆空间碎片问题。因此，如何快捷准确的找到分配大对象的
方法并加以改进是解决这类问题的关键。<br />
本文会给出一个工具（swprof），针对WAS给出定位分配大对象的方法。需要注意的是该工具的启用可能会消耗大量的CPU计算资源，所以建议在测试环境中使用。
<p>当用户在使用WAS的时候碰到了分配大对象引起的碎片问题。找到造成分配失败的线程堆栈信息对于解决问题将会是非常有帮助的。本文提供的工具可以帮助用户：<br />
● 打印分配失败的线程堆栈信息<br />
● 通过设置分配失败的内存大小，限制堆栈信息的打印<br />
● 通过设置方法调用的深度，限制堆栈信息的打印<br />
需要注意的是，这个工具在某些情况下可能无法报告堆栈中方法的准确名字。在这种情况下，用户需要通过其他机制找到大对象的分配方法或者联系本地的IBM技术支持。</p>
<p>为了使用本工具，用户首先需要按照下面的步骤配置java虚拟机，使其能够加载所需的类库：<br />
1．从下面的连接中找到并下载Profiler.zip。展开这个压缩文件，可以看到Profiler.zip包含多个压缩文件。找到对应操作系统的压缩
文件，比如：swprof_Windows.zip对应Windows操作系统，展开并得到对应的类库文件，比如：libswprof.so或者
liballocprof.so或者swprofiler.dll。拷贝类库文件到&lt;WAS_Home&gt;"java"jre"bin路径下。<br />
http://www-1.ibm.com/support/docview.wss?uid=swg21162314&amp;aid=1<br />
其中&lt;WAS_Home&gt;代表WAS的安装目录。<br />
注意：这些类库文件针对每个操作系统是不同的，确保找到与操作系统对应的类库文件并拷贝到指定的路径下。<br />
2．对于AIX、Linux、Linux on
zSeries、Windows平台上的WAS，在Java虚拟机的命令行参数中添加：-Xrunswprof。对于Solaris平台上的WAS，在
Java虚拟机的命令行参数中添加：-Xrunallocprof。添加的具体办法请参考WAS的信息中心：<br />
http://www-306.ibm.com/software/webservers/appserv/was/library/<br />
3．重新启动应用服务器<br />
4．如果，类库被成功的加载，那么在应用服务器启动过程的stderr日志中（比如：native_stderr.log）可以看到类似如下的信息：<br />
swprofiler loaded OK<br />
Allocation limit: XXXX, Depth: YYY<br />
为了更好的利用该工具，用户可以定制打印堆栈的分配大小和深度。其中分配大小通过设定环境变量ALLOC_LIMIT来定制；分配深度通过设定变量
ALLOC_DEPTH来定制。每当应用程序尝试分配超过分配限制大小的对象时，工具就会在应用服务器的stderr日志中（比
如：native_stderr.log）打印申请这个对象的方法信息。分配深度控制着堆栈被打印的层数。两者的默认值是50000和20。这就意味着，
默认情况下当某个应用尝试分配超过50000个字节大小的对象时，工具将打印堆栈的头20层方法。例如：<br />
&#8230;&#8230;<br />
&lt;AF[591]: Allocation Failure. need 22616440 bytes, 6476 ms since last <br />
AF&gt; <br />
&lt;AF[591]: managing allocation failure, action=2 (118062832/674101760)&gt; <br />
&lt;GC(591): GC cycle started Fri Dec 09 08:19:50 2005 <br />
&lt;GC(591): freed 481241408 bytes, 88% free (599304240/674101760), in <br />
105 ms&gt; <br />
&lt;GC(591): mark: 90 ms, sweep: 15 ms, compact: 0 ms&gt; <br />
&lt;GC(591): refs: soft 3 (age &gt;= 32), weak 0, final 21, phantom 1&gt; <br />
&lt;AF[591]: completed in 106 ms&gt; <br />
Large object allocated: size 22616428 <br />
at testalloc in class com/test/OOMTest 21616424 <br />
at service in class javax/servlet/http/HttpServlet<br />
&#8230;&#8230;<br />
用户可以按照如下的方法设定这2个环境变量：<br />
WAS V6.0：<br />
1． 登陆WAS管理控制台依次选择：<br />
服务器 &gt; 应用服务器 &gt; 服务器名称（比如：server1）&gt; Java和进程管理 &gt; 进程定义 &gt; 环境条目 &gt; 新建<br />
2． 添加如下的环境变量和值：<br />
名称 值<br />
ALLOC_LIMIT 限定的字节数（比如：600000）<br />
ALLOC_DEPTH 限定的堆栈深度（比如：10）<br />
3． 保存设置重新启动应用服务器</p>
<p>WAS V5.0 &amp; 5.1：<br />
1． 登陆WAS管理控制台依次选择：<br />
服务器 &gt; 应用服务器 &gt; 服务器名称（比如：server1）&gt; 进程定义 &gt; 环境条目 &gt; 新建<br />
2． 添加如下的环境变量和值：<br />
名称 值<br />
ALLOC_LIMIT 限定的字节数（比如：600000）<br />
ALLOC_DEPTH 限定的堆栈深度（比如：10）<br />
3． 保存设置重新启动应用服务器</p>
<p>Unix/Linux平台上的WAS V4.0 &amp; 4.1：<br />
1． 添加类似如下的2行在startupServer.sh脚本文件的开头，这个文件位于&lt;WAS_Home&gt;/bin路径下：<br />
export ALLOC_LIMIT=600000<br />
export ALLOC_DEPTH=10<br />
2． 重新启动应用服务器</p>
<p>Windows平台上的WAS V4.0 &amp; 4.1：<br />
1．添加类似如下的2行在adminserver.bat脚本文件的开头，这个文件位于&lt;WAS_Home&gt;/bin路径下：<br />
SET ALLOC_LIMIT=600000<br />
SET ALLOC_DEPTH=10<br />
2． 重新启动应用服务器</p>
<img src ="http://www.blogjava.net/spec-second/aggbug/195664.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/spec-second/" target="_blank">BigOnion</a> 2008-04-24 17:50 <a href="http://www.blogjava.net/spec-second/articles/195664.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>