﻿<?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-CONAN ZONE-文章分类-JVM</title><link>http://www.blogjava.net/conans/category/45963.html</link><description>你越挣扎我就越兴奋</description><language>zh-cn</language><lastBuildDate>Sat, 03 Dec 2011 23:03:50 GMT</lastBuildDate><pubDate>Sat, 03 Dec 2011 23:03:50 GMT</pubDate><ttl>60</ttl><item><title>Hot Spot JVM5中的GC调优</title><link>http://www.blogjava.net/conans/articles/365427.html</link><dc:creator>CONAN</dc:creator><author>CONAN</author><pubDate>Fri, 02 Dec 2011 13:59:00 GMT</pubDate><guid>http://www.blogjava.net/conans/articles/365427.html</guid><description><![CDATA[<strong>引言<br />
</strong>有JAVA开发经验的朋友们一定碰到过下面的这种情况,那就是自己所开发的应用运行了一段时间后其性能或者响应速度会有明显的降低.这是由多方面的原因造成的即有程序本身的优化问题,也有运行环境问题.此运行环境即包括硬件环境也包括软件环境.大多数人第一个能想到的解决方法是提升硬件的配置而忽略了程序本身的运行环境JVM也提供了比较多的调优选项.本文将重点描述利用JVM的一些选项对GC进行调优.
<h2></h2>
<p><strong>约定:</strong><br />
1.读者应具备一定JAVA的知识.</p>
<p>2.本文中的JVM选项均以SUN公司发布的HotSpot JVM 5为准（不过大多数的选项在JVM1.3,JVM1.4中也是可用的）.</p>
<p>3.以JAVA_HOME下demo/jfc/SwingSet2/SwingSet2.jar为例进行说明.</p>
<p>4.阅读本文需要一些关于GC的知识，可以到附录A中了解这些知识。</p>
<p><strong>关键字:</strong><br />
JVM(java虚拟机),调优,GC(垃圾回收)</p>
<p><strong>JVM GC调优</strong><br />
为了能够将JVM GC的调优能够使用在具体的实践当中,下面将利用若干个例子来说明GC的调优.<br />
<strong>例1：Heap size 设置</strong><br />
JVM堆的设置是指java程序运行过程中JVM可以调配使用的内存空间的设置.JVM在启动的时候会自动设置Heap size的值，其初始空间(即-Xms)是物理内存的1/64，最大空间(-Xmx)是物理内存的1/4。可以利用JVM提供的-Xmn -Xms -Xmx等选项可进行设置。Heap size 的大小是Young Generation 和Tenured Generaion 之和。<br />
当在JAVA_HOME下demo/jfc/SwingSet2/目录下执行下面的命令。<br />
java -jar -Xmn4m -Xms16m -Xmx16m SwingSet2.jar<br />
系统输出为：<br />
Exception in thread "Image Fetcher 0" java.lang.OutOfMemoryError: Java heap space<br />
Exception in thread "Image Fetcher 3" java.lang.OutOfMemoryError: Java heap space<br />
Exception in thread "Image Fetcher 1" java.lang.OutOfMemoryError: Java heap space<br />
Exception in thread "Image Fetcher 2" java.lang.OutOfMemoryError: Java heap space<br />
除了这些异常信息外，还会发现程序的响应速度变慢了。这说明Heap size 设置偏小，GC占用了更多的时间，而应用分配到的执行时间较少。<br />
<font color="#ff0000">提示：在JVM中如果98％的时间是用于GC且可用的Heap size 不足2％的时候将抛出此异常信息。<br />
</font>将上面的命令换成以下命令执行则应用能够正常使用，且未抛出任何异常。<br />
java -jar -Xmn4m -Xms16m -Xmx32m SwingSet2.jar<br />
<font color="#ff0000">提示：Heap Size 最大不要超过可用物理内存的80％，一般的要将-Xms和-Xmx选项设置为相同，而-Xmn为1/4的-Xmx值。</font></p>
<p><strong>例2：Young Generation（-Xmn）的设置</strong><br />
在本例中看一下Young Generation的设置不同将有什么现象发生。<br />
假设将Young generation 的大小设置为4M ，即执行java -jar -verbose:gc -Xmn4m -Xms32m -Xmx32m -XX:+PrintGCDetails SwingSet2.jar，屏幕输出如下（节选）<br />
[GC [DefNew: 3968K-&gt;64K(4032K), 0.0923407 secs] 3968K-&gt;2025K(32704K), 0.0931870 secs]<br />
[GC [DefNew: 4021K-&gt;64K(4032K), 0.0356847 secs] 5983K-&gt;2347K(32704K), 0.0365441 secs]<br />
[GC [DefNew: 3995K-&gt;39K(4032K), 0.0090603 secs] 6279K-&gt;2372K(32704K), 0.0093377 secs]<br />
[GC [DefNew: 3992K-&gt;23K(4032K), 0.0057540 secs] 6325K-&gt;2356K(32704K), 0.0060290 secs]<br />
[GC [DefNew: 3984K-&gt;27K(4032K), 0.0013058 secs] 6317K-&gt;2360K(32704K), 0.0015888 secs]<br />
[GC [DefNew: 3981K-&gt;59K(4032K), 0.0023307 secs] 6315K-&gt;2422K(32704K), 0.0026091 secs]<br />
将程序体制并将Young Generation的大小设置为8M，即执行java -jar -verbose:gc -Xmn8m -Xms32m -Xmx32m -XX:+PrintGCDetails SwingSet2.jar，屏幕输出如下（节选）<br />
[GC [DefNew: 7808K-&gt;192K(8000K), 0.1016784 secs] 7808K-&gt;2357K(32576K), 0.1022834 secs]<br />
[GC [DefNew: 8000K-&gt;70K(8000K), 0.0149659 secs] 10165K-&gt;2413K(32576K), 0.0152557 secs]<br />
[GC [DefNew: 7853K-&gt;59K(8000K), 0.0069122 secs] 10196K-&gt;2403K(32576K), 0.0071843 secs]<br />
[GC [DefNew: 7867K-&gt;171K(8000K), 0.0075745 secs] 10211K-&gt;2681K(32576K), 0.0078376 secs]<br />
[GC [DefNew: 7970K-&gt;192K(8000K), 0.0201353 secs] 10480K-&gt;2923K(32576K), 0.0206867 secs]<br />
[GC [DefNew: 7979K-&gt;30K(8000K), 0.1787079 secs] 10735K-&gt;4824K(32576K), 0.1790065 secs]<br />
那么根据GC输出的信息（这里取第一行）做一下Minor收集的比较。可以看出两次的Minor收集分别在Young generation中找回3904K（3968K-&gt;64K）和7616K(7808K-&gt;192K)而对于整个jvm则找回1943K（3968K-&gt;2025）和5451K（7808K-&gt;2357K）。第一种情况下Minor收集了大约50％（1943/3904）的对象，而另外的50％的对象则被移到了tenured generation。在第二中情况下Minor收集了大约72％的对象，只有不到30％的对象被移到了Tenured Generation.这个例子说明此应用在的Young generation 设置为4m时显的偏小。<br />
<font color="#ff0000">提示：一般的Young Generation的大小是整个Heap size的1/4。Young generation的minor收集率应一般在70％以上。当然在实际的应用中需要根据具体情况进行调整。</font></p>
<p><strong>例3：Young Generation对应用响应的影响</strong><br />
还是使用-Xmn4m 和-Xmn8m进行比较，先执行下面的命令<br />
</p>
<p>java -jar -verbose:gc -Xmn4m -Xms32m -Xmx32m -XX:+PrintGCDetails -XX:+PrintGCApplicationConcurrentTime -XX:+PrintGCApplicationStoppedTime SwingSet2.jar<br />
屏幕输出如下（节选）<br />
Application time: 0.5114944 seconds<br />
[GC [DefNew: 3968K-&gt;64K(4032K), 0.0823952 secs] 3968K-&gt;2023K(32704K), 0.0827626 secs]<br />
Total time for which application threads were stopped: 0.0839428 seconds<br />
Application time: 0.9871271 seconds<br />
[GC [DefNew: 4020K-&gt;64K(4032K), 0.0412448 secs] 5979K-&gt;2374K(32704K), 0.0415248 secs]<br />
Total time for which application threads were stopped: 0.0464380 seconds<br />
Young Generation 的Minor收集占用的时间可以计算如下：应用线程被中断的总时常/（应用执行总时?L+应用线程被中断的总时常），那么在本例中垃圾收集占用的时?L约为系统的5％~14％。那么当垃圾收集占用的时间的比例越大的时候，系统的响应将越慢。<br />
<font color="#ff0000">提示：对于互联网应用系统的响应稍微慢一些，用户是可以接受的，但是对于GUI类型的应用响应速度慢将会给用户带来非常不好的体验。</font></p>
<p><strong>例4：如何决定Tenured Generation 的大小</strong><br />
分别以-Xmn8m -Xmx32m和-Xmn8m -Xmx64m进行对比，先执行<br />
java -verbose:gc -Xmn8m -Xmx32m-XX:+PririntGCDetails -XX:+PrintGCTimeStamps java类，命令行将提示（只提取了Major收集）<br />
</p>
<p>111.042: [GC 111.042: [DefNew: 8128K-&gt;8128K(8128K), 0.0000505 secs]111.042: [Tenured: 18154K-&gt;2311K(24576K), 0.1290354 secs] 26282K-&gt;2311K(32704K), 0.1293306 secs]<br />
122.463: [GC 122.463: [DefNew: 8128K-&gt;8128K(8128K), 0.0000560 secs]122.463: [Tenured: 18630K-&gt;2366K(24576K), 0.1322560 secs] 26758K-&gt;2366K(32704K), 0.1325284 secs]<br />
133.896: [GC 133.897: [DefNew: 8128K-&gt;8128K(8128K), 0.0000443 secs]133.897: [Tenured: 18240K-&gt;2573K(24576K), 0.1340199 secs] 26368K-&gt;2573K(32704K), 0.1343218 secs]<br />
144.112: [GC 144.112: [DefNew: 8128K-&gt;8128K(8128K), 0.0000544 secs]144.112: [Tenured: 16564K-&gt;2304K(24576K), 0.1246831 secs] 24692K-&gt;2304K(32704K), 0.1249602 secs]<br />
再执行java -verbose:gc -Xmn8m -Xmx64m-XX:+PririntGCDetails -XX:+PrintGCTimeStamps java类，命令行将提示（只提取了Major收集）<br />
90.597: [GC 90.597: [DefNew: 8128K-&gt;8128K(8128K), 0.0000542 secs]90.597: [Tenured: 49841K-&gt;5141K(57344K), 0.2129882 secs] 57969K-&gt;5141K(65472K), 0.2133274 secs]<br />
120.899: [GC 120.899: [DefNew: 8128K-&gt;8128K(8128K), 0.0000550 secs]120.899: [Tenured: 50384K-&gt;2430K(57344K), 0.2216590 secs] 58512K-&gt;2430K(65472K), 0.2219384 secs]<br />
153.968: [GC 153.968: [DefNew: 8128K-&gt;8128K(8128K), 0.0000511 secs]153.968: [Tenured: 51164K-&gt;2309K(57344K), 0.2193906 secs] 59292K-&gt;2309K(65472K), 0.2196372 secs]<br />
可以看出在Heap size 为32m的时候系统等候时间约为0.13秒左右，而设置为64m的时候等候时间则增大到0.22秒左右了。但是在32m的时候系统的Major收集间隔为10秒左右，而Heap size 增加到64m的时候为30秒。那么应用在运行的时候是选择32m还是64m呢？如果应用是web类型（即要求有大的吞吐量）的应用则使用64m（即heapsize大一些）的比较好。对于要求实时响应要求较高的场合（例如GUI型的应用）则使用32m比较好一些。&nbsp;<br />
<font color="#ff0000">注意：<br />
1。因为在JVM5运行时已经对Heap-size进行了优化，所以在能确定java应用运行时不会超过默认的Heap size的情况下建议不要对这些值进行修改。<br />
2。Heap size的 -Xms -Xmn 设置不要超出物理内存的大小。否则会提示&#8220;Error occurred during initialization of VM Could not reserve enough space for object heap&#8221;。</font></p>
<p><strong>例5:如何缩短minor收集的时间</strong><br />
下面比较一下采用-XX:+UseParNewGC选项和不采用它的时候的minor收集将有什么不同。先执行<br />
java -jar -server -verbose:gc -Xmn8m -Xms32m -Xmx32m SwingSet2.jar&nbsp;<br />
系统将输出如下信息（片段〕<br />
[GC 7807K-&gt;2641K(32576K), 0.0676654 secs]<br />
[GC 10436K-&gt;3108K(32576K), 0.0245328 secs]<br />
[GC 10913K-&gt;3176K(32576K), 0.0072865 secs]<br />
[GC 10905K-&gt;4097K(32576K), 0.0223928 secs]<br />
之后再执行 java -jar -server -verbose:gc -XX:+UseParNewGC -Xmn8m -Xms32m -Xmx32m SwingSet2.jar<br />
系统将输出如下信息（片段〕<br />
[ParNew 7808K-&gt;2656K(32576K), 0.0447687 secs]<br />
[ParNew 10441K-&gt;3143K(32576K), 0.0179422 secs]<br />
[ParNew 10951K-&gt;3177K(32576K), 0.0031914 secs]<br />
[ParNew 10985K-&gt;3867K(32576K), 0.0154991 secs]<br />
很显然使用了-XX:+UseParNewGC选项的minor收集的时间要比不使用的时候优。</p>
<p><strong>例6:如何缩短major收集的时间</strong><br />
下面比较一下采用-XX:+UseConcMarkSweepGC选项和不采用它的时候的major收集将有什么不同。先执行<br />
java -jar -verbose:gc -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -Xmn64m -Xms256m -Xmx256m SwingSet2.jar<br />
系统将输出如下信息（片段〕<br />
[Full GC 22972K-&gt;18690K(262080K), 0.2326676 secs]<br />
[Full GC 18690K-&gt;18690K(262080K), 0.1701866 secs<br />
之后再执行 java -jar -verbose:gc -XX:+UseParNewGC -Xmn64m -Xms256m -Xmx256m SwingSet2.jar<br />
系统将输出如下信息（片段〕<br />
[Full GC 56048K-&gt;18869K(260224K), 0.3104852 secs]<br />
<font color="#ff0000">提示：此选项在Heap Size 比较大而且Major收集时间较长的情况下使用更合适。</font></p>
<p><strong>例7:关于-server选项</strong>&nbsp;在JVM中将运行中的类认定为server-class的时候使用此选项。SUN 的Hot Spot JVM5 如果判断到系统的配置满足如下条件则自动将运行的类认定为server-class，并且会自动设置jvm的选项（当没有手工设置这选项的时候〕而且HOTSPOT JVM5提供了自动调优的功能，他会根据JVM的运行情况进行调整。如果没有特别的需要是不需要太多的人工干预的。SUN形象的称这个机制为&#8220;人体工学&#8221;（Ergonomics〕。具体可以参考http://java.sun.com/docs/hotspot/gc5.0/ergo5.html<br />
*.具有2个或更多个物理的处理器<br />
*.具有2G或者更多的物理内存<br />
<font color="#ff0000">提示：此选项要放在所有选项的前面。例如：java -server 其他选项 java类</font></p>
<p><strong>附录A:预备知识</strong><br />
<strong>．</strong>JVM中对象的划分及管理</p>
<p>JVM根据运行于其中的对象的生存时间大致的分为3种。并且将这3种不同的对象分别存放在JVM从系统分配到的不同的内存空间。这种对象存放空间的管理方式叫做Generation管理方式。<br />
1。Young Generation：用于存放&#8220;早逝&#8221;对象（即瞬时对象）。例如：在创建对象时或者调用方法时使用的临时对象或局部变量。<br />
2。Tenured Generation：用于存放&#8220;驻留&#8221;对象（即较长时间被引用的对象）。往往体现为一个大型程序中的全局对象或长时间被使用的对象。<br />
3。Perm Generation：用于存放&#8220;永久&#8221;对象。这些对象管理着运行于JVM中的类和方法。</p>
<p><strong>．</strong>JVM选项的分类</p>
<p>JVM有这么几种选项供使用.<br />
1.供-X选项使用的项目,又称为非标准选项，不同厂商的此类型选项是有所不同的。例如：IBM的JVM用的一些选项在Sun的JVM中就不一定能生效。这种选项的使用方式如下:<br />
java -Xmn16m -Xms64m -Xmx64m java类名<br />
2.供-XX选项使用的项目，这种类型的选项可能要求有对系统信息访问的权限。所以要慎用。这种选项的使用方式如下:<br />
java -XX:MaxHeapFreeRatio=70 -XX:+PrintGCDetails java类名<br />
3.java选项(即在命令行执行java后提示的选项).<br />
java -server -verbose:gc -d64 java类名</p>
<p><strong>．</strong>垃圾收集分类</p>
<p>在JVM中有两种垃圾方式，一种叫做Minor（次收集），另一种叫做Major（主收集）。其中Minor在Young Generation的空间被对象全部占用后执行，主要是对Young Generation中的对象进行垃圾收集。而Major是针对于整个Heap size的垃圾收集。其中Minor方式的收集经常发生，并且Minor收集所占用的系统时间小。Major方式的垃圾收集则是一种&#8220;昂贵&#8221;的垃圾收集方式，因为在Major要对整个Heap size进行垃圾收集,这会使得应用停顿的时间变得较长。</p>
<p><strong>．</strong>GC信息的格式</p>
<p>&nbsp;</p>
<p>[GC [&lt;collector&gt;: &lt;starting occupancy1&gt; -&gt; &lt;ending occupancy1&gt;, &lt;pause time1&gt; secs] &lt;starting occupancy3&gt; -&gt; &lt;ending occupancy3&gt;, &lt;pause time3&gt; secs]<br />
&lt;collector&gt; GC为minor收集过程中使用的垃圾收集器起的内部名称.<br />
&lt;starting occupancy1&gt; young generation 在进行垃圾收集前被对象使用的存储空间.<br />
&lt;ending occupancy1&gt; young generation 在进行垃圾收集后被对象使用的存储空间<br />
&lt;pause time1&gt; minor收集使应用暂停的时间长短(秒)&nbsp;<br />
&lt;starting occupancy3&gt; 整个堆(Heap Size)在进行垃圾收集前被对象使用的存储空间<br />
&lt;ending occupancy3&gt; 整个堆(Heap Size)在进行垃圾收集后被对象使用的存储空间<br />
&lt;pause time3&gt; 整个垃圾收集使应用暂停的时间长短(秒),包括major收集使应用暂停的时间(如果发生了major收集).<br />
<strong>．</strong>GC信息的选项<br />
-XX:+PrintGCDetails 显示GC的详细信息<br />
-XX:+PrintGCApplicationConcurrentTime 打印应用执行的时间<br />
-XX:+PrintGCApplicationStoppedTime 打印应用被暂停的时间<br />
<font color="#ff0000">提示:1.":"后的"+"号表示开启此选项,如果是"-"号那么表示关闭此选项。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2.在不同的选项和不同的收集方式和类型下输出的格式会有所不同。</font></p>
@import url(http://www.blogjava.net/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);<img src ="http://www.blogjava.net/conans/aggbug/365427.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/conans/" target="_blank">CONAN</a> 2011-12-02 21:59 <a href="http://www.blogjava.net/conans/articles/365427.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>jstat使用</title><link>http://www.blogjava.net/conans/articles/365426.html</link><dc:creator>CONAN</dc:creator><author>CONAN</author><pubDate>Fri, 02 Dec 2011 13:30:00 GMT</pubDate><guid>http://www.blogjava.net/conans/articles/365426.html</guid><description><![CDATA[<p>jstat</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1. jstat -gc pid</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 可以显示gc的信息，查看gc的次数，及时间。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 其中最后五项，分别是young gc的次数，young gc的时间，full gc的次数，full gc的时间，gc的总时间。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.jstat -gccapacity pid<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 可以显示，VM内存中三代（young,old,perm）对象的使用和占用大小，</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如：PGCMN显示的是最小perm的内存使用量，PGCMX显示的是perm的内存最大使用量，</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PGC是当前新生成的perm内存占用量，PC是但前perm内存占用量。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 其他的可以根据这个类推， OC是old内纯的占用量。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 3.jstat -gcutil pid</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 统计gc信息统计。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 4.jstat -gcnew pid</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 年轻代对象的信息。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 5.jstat -gcnewcapacity pid</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 年轻代对象的信息及其占用量。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 6.jstat -gcold pid</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; old代对象的信息。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 7.stat -gcoldcapacity pid</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; old代对象的信息及其占用量。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 8.jstat -gcpermcapacity pid</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; perm对象的信息及其占用量。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 9.jstat -class pid</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 显示加载class的数量，及所占空间等信息。<br />
&nbsp;&nbsp;&nbsp;&nbsp; 10.jstat -compiler pid</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 显示VM实时编译的数量等信息。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 11.stat -printcompilation pid</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当前VM执行的信息。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一些术语的中文解释：</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; S0C：年轻代中第一个survivor（幸存区）的容量 (字节)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; S1C：年轻代中第二个survivor（幸存区）的容量 (字节)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; S0U：年轻代中第一个survivor（幸存区）目前已使用空间 (字节)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; S1U：年轻代中第二个survivor（幸存区）目前已使用空间 (字节)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EC：年轻代中Eden（伊甸园）的容量 (字节)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EU：年轻代中Eden（伊甸园）目前已使用空间 (字节)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OC：Old代的容量 (字节)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OU：Old代目前已使用空间 (字节)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PC：Perm(持久代)的容量 (字节)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PU：Perm(持久代)目前已使用空间 (字节)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; YGC：从应用程序启动到采样时年轻代中gc次数<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; YGCT：从应用程序启动到采样时年轻代中gc所用时间(s)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FGC：从应用程序启动到采样时old代(全gc)gc次数<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FGCT：从应用程序启动到采样时old代(全gc)gc所用时间(s)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GCT：从应用程序启动到采样时gc用的总时间(s)</p>
<p>&nbsp;&nbsp;&nbsp; NGCMN：年轻代(young)中初始化(最小)的大小 (字节)</p>
<p>&nbsp;&nbsp;&nbsp; NGCMX：年轻代(young)的最大容量 (字节)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NGC：年轻代(young)中当前的容量 (字节)</p>
<p>&nbsp;&nbsp; OGCMN：old代中初始化(最小)的大小 (字节)</p>
<p>&nbsp;&nbsp; OGCMX：old代的最大容量 (字节)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OGC：old代当前新生成的容量 (字节)</p>
<p>&nbsp;&nbsp; PGCMN：perm代中初始化(最小)的大小 (字节)</p>
<p>&nbsp;&nbsp; PGCMX：perm代的最大容量 (字节)&nbsp;&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PGC：perm代当前新生成的容量 (字节)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; S0：年轻代中第一个survivor（幸存区）已使用的占当前容量百分比</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; S1：年轻代中第二个survivor（幸存区）已使用的占当前容量百分比</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; E：年轻代中Eden（伊甸园）已使用的占当前容量百分比</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; O：old代已使用的占当前容量百分比</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; P：perm代已使用的占当前容量百分比</p>
<p>&nbsp; S0CMX：年轻代中第一个survivor（幸存区）的最大容量 (字节)</p>
<p>&nbsp;S1CMX ：年轻代中第二个survivor（幸存区）的最大容量 (字节)</p>
<p>&nbsp;&nbsp;&nbsp; ECMX：年轻代中Eden（伊甸园）的最大容量 (字节)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DSS：当前需要survivor（幸存区）的容量 (字节)（Eden区已满）</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TT： 持有次数限制</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MTT ： 最大持有次数限制</p>
@import url(http://www.blogjava.net/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);<img src ="http://www.blogjava.net/conans/aggbug/365426.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/conans/" target="_blank">CONAN</a> 2011-12-02 21:30 <a href="http://www.blogjava.net/conans/articles/365426.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JVM调优</title><link>http://www.blogjava.net/conans/articles/339582.html</link><dc:creator>CONAN</dc:creator><author>CONAN</author><pubDate>Thu, 02 Dec 2010 02:53:00 GMT</pubDate><guid>http://www.blogjava.net/conans/articles/339582.html</guid><description><![CDATA[<span id="ctl00_MainContentPlaceholder_ctl01_ctl00_lblEntry">
<p><strong><span style="font-size: x-small;">一、相关概念</span>
</strong>
</p>
<p>
<br />
<strong><span style="color: #ff0000;">基本回收算法</span>
</strong>
</p>
<ol>
    <li>
    <strong>引用计数（Reference Counting）<br />
    </strong>
    比较古老的回收算法。原理是此对象有一个引用，即增加一个计数，删除一个引用则减少一个计数。垃圾回收时，只用收集计数为0的对象。此算法最致命的是无法处理循环引用的问题。  </li>
    <li>
    <strong>标记-清除（Mark-Sweep）<br />
    </strong>
    此算法执行分两阶段。第一阶段从引用根节点开始标记所有被引用的对象，第二阶段遍历整个堆，把未标记的对象清除。此算法需要暂停整个应用，同时，会产生内存碎片。  </li>
    <li>
    <strong>复制（Copying）<br />
    </strong>
    此
    算法把内存空间划为两个相等的区域，每次只使用其中一个区域。垃圾回收时，遍历当前使用区域，把正在使用中的对象复制到另外一个区域中。次算法每次只处理
    正在使用中的对象，因此复制成本比较小，同时复制过去以后还能进行相应的内存整理，不过出现&#8220;碎片&#8221;问题。当然，此算法的缺点也是很明显的，就是需要两倍
    内存空间。  </li>
    <li>
    <strong>标记-整理（Mark-Compact）<br />
    </strong>
    此算法结合了&#8220;标记-清除&#8221;和&#8220;复
    制&#8221;两个算法的优点。也是分两阶段，第一阶段从根节点开始标记所有被引用对象，第二阶段遍历整个堆，把清除未标记对象并且把存活对象&#8220;压缩&#8221;到堆的其中一
    块，按顺序排放。此算法避免了&#8220;标记-清除&#8221;的碎片问题，同时也避免了&#8220;复制&#8221;算法的空间问题。  </li>
    <li>
    <strong>增量收集（Incremental Collecting）</strong>
    <br />
    实施垃圾回收算法，即：在应用进行的同时进行垃圾回收。不知道什么原因JDK5.0中的收集器没有使用这种算法的。  </li>
    <li>
    <strong>分代（Generational Collecting）<br />
    </strong>
    基于对对象生命周期分析后得出的垃圾回收算法。把对象分为年青代、年老代、持久代，对不同生命周期的对象使用不同的算法（上述方式中的一个）进行回收。现在的垃圾回收器（从J2SE1.2开始）都是使用此算法的。 </li>
</ol>
<br />
<strong><span style="color: #ff0000;">分代垃圾回收详述</span>
</strong>
<p><img src="http://lh3.google.com/pengjiaheng/Ron7MqXPDPI/AAAAAAAAAH0/Ulzfr9RZX9o/Java%20SE%206%20HotSpot%5Btm%5D%20Virtual%20Machine%20Garbage%20Collection%20Tuning_1183357573109.JPG" alt="" height="425" width="654" /><br />
如上图所示，为Java堆中的各代分布。  </p>
<ol>
    <li>
    <strong>Young（年轻代）<br />
    </strong>
    年
    轻代分三个区。一个Eden区，两个Survivor区。大部分对象在Eden区中生成。当Eden区满时，还存活的对象将被复制到Survivor区
    （两个中的一个），当这个Survivor区满时，此区的存活对象将被复制到另外一个Survivor区，当这个Survivor去也满了的时候，从第一
    个Survivor区复制过来的并且此时还存活的对象，将被复制&#8220;年老区(Tenured)&#8221;。需要注意，Survivor的两个区是对称的，没先后关
    系，所以同一个区中可能同时存在从Eden复制过来
    对象，和从前一个Survivor复制过来的对象，而复制到年老区的只有从第一个Survivor去过来的对象。而且，Survivor区总有一个是空
    的。  </li>
    <li>
    <strong>Tenured（年老代）<br />
    </strong>
    年老代存放从年轻代存活的对象。一般来说年老代存放的都是生命期较长的对象。  </li>
    <li>
    <strong>Perm（持久代）<br />
    </strong>
    用
    于存放静态文件，如今Java类、方法等。持久代对垃圾回收没有显著影响，但是有些应用可能动态生成或者调用一些class，例如Hibernate等，
    在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过-XX:MaxPermSize=&lt;N&gt;进行设置。</li>
</ol>
<br />
<strong><span style="color: #ff0000;">GC类型</span>
<br />
</strong>
GC有两种类型：<strong>Scavenge GC和Full GC</strong>
。
<ol>
    <li>Scavenge GC<br />
    一般情况下，当新对象生成，并且在Eden申请空间失败时，就好触发Scavenge GC，堆Eden区域进行GC，清除非存活对象，并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。  </li>
    <li>Full GC<br />
    对整个堆进行整理，包括Young、Tenured和Perm。Full GC比Scavenge GC要慢，因此应该尽可能减少Full GC。有如下原因可能导致Full GC：
    <ul>
        <li>Tenured被写满 </li>
    </ul>
    <ul>
        <li>Perm域被写满 </li>
    </ul>
    <ul>
        <li>System.gc()被显示调用 </li>
    </ul>
    <ul>
        <li>上一次GC之后Heap的各域分配策略动态变化 </li>
    </ul>
    </li>
</ol>
<p><br />
<strong><span style="color: #ff0000;">分代垃圾回收过程演示</span>
</strong>
<br />
<img src="http://lh4.google.com/pengjiaheng/RosphaXPDZI/AAAAAAAAAJY/5g7J7TqR2pI/1.JPG?imgmax=576" alt="" /><br />
<img src="http://lh4.google.com/pengjiaheng/RosphaXPDaI/AAAAAAAAAJg/k1Z_u32zD0Y/2.JPG?imgmax=576" alt="" /><br />
<img src="http://lh4.google.com/pengjiaheng/RosphaXPDbI/AAAAAAAAAJo/e3bRlUcld3Q/3.JPG?imgmax=576" alt="" /><br />
<img src="http://lh4.google.com/pengjiaheng/RosphaXPDcI/AAAAAAAAAJw/Q-M__ID-k_0/4.JPG?imgmax=576" alt="" /></p>
<p><br />
<strong><span style="font-size: x-small;">二、垃圾回收器</span>
</strong>
</p>
<p><strong><span style="font-size: x-small;"><br />
</span>
</strong>
目前的收集器主要有三种：<strong>串行收集器、并行收集器、并发收集器</strong>
。</p>
<ol>
    <li>
    <strong>串行收集器</strong>
    <br />
    <img src="http://lh3.google.com/pengjiaheng/Ron7MqXPDTI/AAAAAAAAAIU/FKFnXwdLz-4/%E4%B8%B2%E8%A1%8C%E6%94%B6%E9%9B%86%E5%99%A8.JPG" alt="" height="390" width="713" /><br />
    使用单线程处理所有垃圾回收工作，因为无需多线程交互，所以效率比较高。但是，也无法使用多处理器的优势，所以此收集器适合单处理器机器。当然，此收集器也可以用在小数据量（<strong>100M</strong>
    左右）情况下的多处理器机器上。可以使用<strong>-XX:+UseSerialGC</strong>
    打开。<br />
    </li>
    <li>
    <strong>并行收集器<br />
    </strong>
    <img title="点击查看原始大小图片" src="http://lh3.google.com/pengjiaheng/Ron7MqXPDSI/AAAAAAAAAIM/YClzvXUk5A4/%E5%B9%B6%E8%A1%8C%E6%94%B6%E9%9B%86%E5%99%A8.JPG" alt="" height="405" width="700" />
    <ol>
        <li>对年轻代进行并行垃圾回收，因此可以减少垃圾回收时间。一般在多线程多处理器机器上使用。使用<strong>-XX:+UseParallelGC</strong>
        .打开。并行收集器在J2SE5.0第六6更新上引入，在Java SE6.0中进行了增强--可以堆年老代进行并行收集。<strong>如果年老代不使用并发收集的话，是使用单线程进行垃圾回收</strong>
        ，因此会制约扩展能力。使用<strong>-XX:+UseParallelOldGC</strong>
        打开。  </li>
        <li>使用<strong>-XX:ParallelGCThreads=&lt;N&gt;</strong>
        设置并行垃圾回收的线程数。<strong>此值可以设置与机器处理器数量相等</strong>
        。  </li>
        <li>此收集器可以进行如下配置：
        <ul>
            <li>
            <strong>最大垃圾回收暂停:</strong>
            指定垃圾回收时的最长暂停时间，通过<strong>-XX:MaxGCPauseMillis=&lt;N&gt;</strong>
            指定。&lt;N&gt;为毫秒.如果指定了此值的话，<strong>堆大小和垃圾回收相关参数会进行调整以达到指定值</strong>
            。设定此值可能会减少应用的吞吐量。  </li>
            <li>
            <strong>吞吐量:</strong>
            吞吐量为<strong>垃圾回收时间与非垃圾回收时间的比值</strong>
            ，通过<strong>-XX:GCTimeRatio=&lt;N&gt;</strong>
            来设定，公式为<strong>1/（1+N）</strong>
            。例如，-XX:GCTimeRatio=19时，表示5%的时间用于垃圾回收。默认情况为99，即1%的时间用于垃圾回收。</li>
        </ul>
        </li>
    </ol>
    </li>
    <li>
    <strong>并发收集器<br />
    </strong>
    可以保证大部分工作都并发进行（应用不停止），垃圾回收只暂停很少的时间，此收集器适合对响应时间要求比较高的中、大规模应用。使用<strong>-XX:+UseConcMarkSweepGC</strong>
    打开。<br />
    <img title="点击查看原始大小图片" src="http://lh3.google.com/pengjiaheng/Ron7MqXPDRI/AAAAAAAAAIE/HnA7UnjlqQ4/%E5%B9%B6%E5%8F%91%E6%94%B6%E9%9B%86%E5%99%A8.JPG" alt="" height="346" width="700" />
    <ol>
        <li>并
        发收集器主要减少年老代的暂停时间，他在应用不停止的情况下使用独立的垃圾回收线程，跟踪可达对象。在每个年老代垃圾回收周期中，在收集初期并发收集器会
        对整个应用进行简短的暂停，在收集中还会再暂停一次。第二次暂停会比第一次稍长，在此过程中多个线程同时进行垃圾回收工作。  </li>
        <li>并发收集器使用<strong>处理器换来短暂的停顿时间</strong>
        。在一个N个处理器的系统上，并发收集部分使用<strong>K/N</strong>
        个可用处理器进行回收，一般情况下<strong>1&lt;=K&lt;=N/4</strong>
        。  </li>
        <li>在只有<strong>一个处理器的主机上使用并发收集器</strong>
        ，设置为<strong>incremental mode</strong>
        模式也可获得较短的停顿时间。  </li>
        <li>
        <strong>浮动垃圾</strong>
        ：由于在应用运行的同时进行垃圾回收，所以有些垃圾可能在垃圾回收进行完成时产生，这样就造成了&#8220;Floating Garbage&#8221;，这些垃圾需要在下次垃圾回收周期时才能回收掉。所以，并发收集器一般需要<strong>20%</strong>
        的预留空间用于这些浮动垃圾。  </li>
        <li>
        <strong>Concurrent Mode Failure</strong>
        ：并发收集器在应用运行时进行收集，所以需要保证堆在垃圾回收的这段时间有足够的空间供程序使用，否则，垃圾回收还未完成，堆空间先满了。这种情况下将会发生&#8220;并发模式失败&#8221;，此时整个应用将会暂停，进行垃圾回收。  </li>
        <li>
        <strong>启动并发收集器</strong>
        ：因为并发收集在应用运行时进行收集，所以必须保证收集完成之前有足够的内存空间供程序使用，否则会出现&#8220;Concurrent Mode Failure&#8221;。通过设置<strong>-XX:CMSInitiatingOccupancyFraction=&lt;N&gt;</strong>
        指定还有多少剩余堆时开始执行并发收集</li>
    </ol>
    </li>
    <li>
    <strong>小结</strong>
    <ul>
        <li>
        <strong>串行处理器：</strong>
        <br />
        &nbsp;--适用情况：数据量比较小（100M左右）；单处理器下并且对响应时间无要求的应用。<br />
        &nbsp;--缺点：只能用于小型应用  </li>
        <li>
        <strong>并行处理器：</strong>
        <br />
        &nbsp;--适用情况：&#8220;对吞吐量有高要求&#8221;，多CPU、对应用响应时间无要求的中、大型应用。举例：后台处理、科学计算。<br />
        &nbsp;--缺点：应用响应时间可能较长  </li>
        <li>
        <strong>并发处理器：<br />
        </strong>
        &nbsp;--适用情况：&#8220;对响应时间有高要求&#8221;，多CPU、对应用响应时间有较高要求的中、大型应用。举例：Web服务器/应用服务器、电信交换、集成开发环境。</li>
    </ul>
    </li>
</ol>
<br />
<strong><span style="font-size: x-small;">三、常见配置举例</span>
</strong>
<ol>
    <li>
    <strong>堆大小设置<br />
    </strong>
    JVM
    中最大堆大小有三方面限制：相关操作系统的数据模型（32-bt还是64-bit）限制；系统的可用虚拟内存限制；系统的可用物理内存限制。32位系统
    下，一般限制在1.5G~2G；64为操作系统对内存无限制。我在Windows Server 2003
    系统，3.5G物理内存，JDK5.0下测试，最大可设置为1478m。<br />
    <code><strong>典型设置：</strong>
    </code>
    <ul>
        <li>
        <code>java <strong>-Xmx3550m -Xms3550m -Xmn2g</strong>
        </code>
        <code><strong>-Xss128k</strong>
        <br />
        </code>
        <code><strong>-</strong>
        </code>
        <code><strong>Xmx3550m</strong>
        ：设置JVM最大可用内存为3550M。<br />
        </code>
        <code><strong>-Xms3550m</strong>
        </code>
        <code>：设置JVM促使内存为3550m。此值可以设置与-Xmx相同，以避免每次垃圾回收完成后JVM重新分配内存。<br />
        </code>
        <code><strong><span style="color: #ff0000;">-Xmn2g</span>
        </strong>
        </code>
        <code>：设置年轻代大小为2G。<strong><span style="color: #0000ff;">整个堆大小=年轻代大小 + 年老代大小 + 持久代大小</span>
        </strong>
        。持久代一般固定大小为64m，所以增大年轻代后，将会减小年老代大小。此值对系统性能影响较大，Sun官方推荐配置为整个堆的3/8。<br />
        </code>
        <code><strong>-Xss128k</strong>
        </code>
        <code>：
        设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M，以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内
        存下，减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的，不能无限生成，经验值在3000~5000左右。<br />
        </code>
        </li>
        <li>
        <code>java -Xmx3550m -Xms3550m </code>
        <code>-Xss128k <strong>-XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0</strong>
        </code>
        <br />
        <code><strong>-XX:NewRatio=4</strong>
        </code>
        <code>:设置年轻代（包括Eden和两个Survivor区）与年老代的比值（除去持久代）。设置为4，则年轻代与年老代所占比值为1：4，年轻代占整个堆栈的1/5<br />
        </code>
        <code><strong>-XX:SurvivorRatio=4</strong>
        </code>
        ：设置年轻代中Eden区与Survivor区的大小比值。设置为4，则两个Survivor区与一个Eden区的比值为2:4，一个Survivor区占整个年轻代的1/6<br />
        <code><strong>-XX:MaxPermSize=16m</strong>
        </code>
        :设置持久代大小为16m。<br />
        <code><strong>-XX:MaxTenuringThreshold=0</strong>
        </code>
        <code>：设置垃圾最大年龄。<strong><span style="color: #0000ff;">如果设置为0的话，则年轻代对象不经过Survivor区，直接进入年老代</span>
        </strong>
        。对于年老代比较多的应用，可以提高效率。<strong><span style="color: #0000ff;">如果将此值设置为一个较大值，则年轻代对象会在Survivor区进行多次复制，这样可以增加对象再年轻代的存活时间</span>
        </strong>
        ，增加在年轻代即被回收的概论。</code>
        </li>
    </ul>
    </li>
    <li>
    <strong>回收器选择<br />
    </strong>
    JVM给了三种选择：<strong>串行收集器、并行收集器、并发收集器</strong>
    ，但是串行收集器只适用于小数据量的情况，所以这里的选择主要针对并行收集器和并发收集器。默认情况下，JDK5.0以前都是使用串行收集器，如果想使用其他收集器需要在启动时加入相应参数。JDK5.0以后，JVM会根据当前<a href="http://java.sun.com/j2se/1.5.0/docs/guide/vm/server-class.html">系统配置</a>
    进行判断。
    <ol>
        <li>
        <strong>吞吐量优先</strong>
        的并行收集器<br />
        如上文所述，并行收集器主要以到达一定的吞吐量为目标，适用于科学技术和后台处理等。<br />
        <strong>典型配置</strong>
        ：
        <ul>
            <li>
            <code>java -Xmx3800m -Xms3800m -Xmn2g -Xss128k <strong>-XX:+UseParallelGC -XX:ParallelGCThreads=20</strong>
            <br />
            </code>
            <code><strong>-XX:+UseParallelGC</strong>
            </code>
            <code>：选择垃圾收集器为并行收集器。<strong><span style="color: #0000ff;">此配置仅对年轻代有效。即上述配置下，年轻代使用并发收集，而年老代仍旧使用串行收集。<br />
            </span>
            </strong>
            </code>
            <code><strong>-XX:ParallelGCThreads=20</strong>
            </code>
            <code>：配置并行收集器的线程数，即：同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。<br />
            </code>
            </li>
            <li>
            <code>java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 <strong>-XX:+UseParallelOldGC<br />
            </strong>
            </code>
            <code><strong>-XX:+UseParallelOldGC</strong>
            </code>
            <code>：配置年老代垃圾收集方式为并行收集。JDK6.0支持对年老代并行收集。<br />
            </code>
            </li>
            <li>
            <code>java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC&nbsp; <strong>-XX:MaxGCPauseMillis=100<br />
            </strong>
            </code>
            <code><strong>-XX:MaxGCPauseMillis=100</strong>
            </code>
            <code><strong>:</strong>
            设置每次年轻代垃圾回收的最长时间，如果无法满足此时间，JVM会自动调整年轻代大小，以满足此值。<br />
            </code>
            </li>
            <li>
            <code>java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC&nbsp; -XX:MaxGCPauseMillis=100 </code>
            <strong>-XX:+UseAdaptiveSizePolicy<br />
            -XX:+UseAdaptiveSizePolicy</strong>
            ：设置此选项后，并行收集器会自动选择年轻代区大小和相应的Survivor区比例，以达到目标系统规定的最低相应时间或者收集频率等，此值建议使用并行收集器时，一直打开。<br />
            </li>
        </ul>
        </li>
        <li>
        <strong>响应时间优先</strong>
        的并发收集器<br />
        如上文所述，并发收集器主要是保证系统的响应时间，减少垃圾收集时的停顿时间。适用于应用服务器、电信领域等。<br />
        <strong>典型配置</strong>
        ：
        <ul>
            <li>
            <code>java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 <strong>-XX:+UseConcMarkSweepGC -XX:+UseParNewGC<br />
            </strong>
            </code>
            <code><strong>-XX:+UseConcMarkSweepGC</strong>
            </code>
            <code>：设置年老代为并发收集。测试中配置这个以后，</code>
            <code>-XX:NewRatio=4的配置失效了，原因不明。所以，此时年轻代大小最好用-Xmn设置。</code>
            <br />
            <code><strong>-XX:+UseParNewGC</strong>
            </code>
            <code>:</code>
            <code>设置年轻代为并行收集。可与CMS收集同时使用。JDK5.0以上，JVM会根据系统配置自行设置，所以无需再设置此值。</code>
            </li>
            <li>
            <code>java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC </code>
            <strong><code>-XX:CMSFullGCsBeforeCompaction=5 </code>
            -XX:+UseCMSCompactAtFullCollection</strong>
            <br />
            <code><strong>-XX:CMSFullGCsBeforeCompaction</strong>
            ：由于并发收集器不对内存空间进行压缩、整理，所以运行一段时间以后会产生&#8220;碎片&#8221;，使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。<br />
            </code>
            <strong>-XX:+UseCMSCompactAtFullCollection</strong>
            ：打开对年老代的压缩。可能会影响性能，但是可以消除碎片<br />
            </li>
        </ul>
        </li>
    </ol>
    </li>
    <li>
    <strong>辅助信息<br />
    </strong>
    JVM提供了大量命令行参数，打印信息，供调试使用。主要有以下一些：
    <ul>
        <li>
        <strong>-XX:+PrintGC<br />
        </strong>
        输出形式<strong>：[GC 118250K-&gt;113543K(130112K), 0.0094143 secs] </strong>
        <p><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Full GC 121376K-&gt;10414K(130112K), 0.0650971 secs]</strong>
        </p>
        </li>
        <li>
        <strong>-XX:+PrintGCDetails<br />
        </strong>
        输出形式<strong>：[GC [DefNew: 8614K-&gt;781K(9088K), 0.0123035 secs] 118250K-&gt;113543K(130112K), 0.0124633 secs] </strong>
        <p><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
        [GC [DefNew: 8614K-&gt;8614K(9088K), 0.0000665 secs][Tenured:
        112761K-&gt;10414K(121024K), 0.0433488 secs]
        121376K-&gt;10414K(130112K), 0.0436268 secs]</strong>
        </p>
        </li>
        <li>
        <strong>-XX:+PrintGCTimeStamps</strong>
        -XX:+PrintGC：PrintGCTimeStamps可与上面两个混合使用<br />
        输出形式：<strong>11.851: [GC 98328K-&gt;93620K(130112K), 0.0082960 secs]<br />
        </strong>
        </li>
        <li>
        <strong>-XX:+PrintGCApplicationConcurrentTime:</strong>
        打印每次垃圾回收前，程序未中断的执行时间。可与上面混合使用<br />
        输出形式：<strong>Application time: 0.5291524 seconds<br />
        </strong>
        </li>
        <li>
        <strong>-XX:+PrintGCApplicationStoppedTime</strong>
        ：打印垃圾回收期间程序暂停的时间。可与上面混合使用<br />
        输出形式：<strong>Total time for which application threads were stopped: 0.0468229 seconds<br />
        </strong>
        </li>
        <li>
        <strong>-XX:PrintHeapAtGC</strong>
        :打印GC前后的详细堆栈信息<br />
        输出形式：<br />
        34.702: [GC {Heap before gc invocations=7:<br />
        &nbsp;def new generation&nbsp;&nbsp; total 55296K, used 52568K [0x1ebd0000, 0x227d0000, 0x227d0000)<br />
        <strong>eden space 49152K,&nbsp; 99% used</strong>
        [0x1ebd0000, 0x21bce430, 0x21bd0000)<br />
        <strong>from space 6144K,&nbsp; 55% used</strong>
        [0x221d0000, 0x22527e10, 0x227d0000)<br />
        &nbsp; to&nbsp;&nbsp; space 6144K,&nbsp;&nbsp; 0% used [0x21bd0000, 0x21bd0000, 0x221d0000)<br />
        &nbsp;tenured generation&nbsp;&nbsp; total 69632K, used 2696K [0x227d0000, 0x26bd0000, 0x26bd0000)<br />
        <strong>the space 69632K,&nbsp;&nbsp; 3% used</strong>
        [0x227d0000, 0x22a720f8, 0x22a72200, 0x26bd0000)<br />
        &nbsp;compacting perm gen&nbsp; total 8192K, used 2898K [0x26bd0000, 0x273d0000, 0x2abd0000)<br />
        &nbsp;&nbsp; the space 8192K,&nbsp; 35% used [0x26bd0000, 0x26ea4ba8, 0x26ea4c00, 0x273d0000)<br />
        &nbsp;&nbsp;&nbsp; ro space 8192K,&nbsp; 66% used [0x2abd0000, 0x2b12bcc0, 0x2b12be00, 0x2b3d0000)<br />
        &nbsp;&nbsp;&nbsp; rw space 12288K,&nbsp; 46% used [0x2b3d0000, 0x2b972060, 0x2b972200, 0x2bfd0000)<br />
        34.735: [DefNew: 52568K-&gt;3433K(55296K), 0.0072126 secs] 55264K-&gt;6615K(124928K)<strong>Heap after gc invocations=8:<br />
        </strong>
        &nbsp;def new generation&nbsp;&nbsp; total 55296K, used 3433K [0x1ebd0000, 0x227d0000, 0x227d0000)<br />
        <strong>eden space 49152K,&nbsp;&nbsp; 0% used</strong>
        [0x1ebd0000, 0x1ebd0000, 0x21bd0000)<br />
        &nbsp; from space 6144K,&nbsp; 55% used [0x21bd0000, 0x21f2a5e8, 0x221d0000)<br />
        &nbsp; to&nbsp;&nbsp; space 6144K,&nbsp;&nbsp; 0% used [0x221d0000, 0x221d0000, 0x227d0000)<br />
        &nbsp;tenured generation&nbsp;&nbsp; total 69632K, used 3182K [0x227d0000, 0x26bd0000, 0x26bd0000)<br />
        <strong>the space 69632K,&nbsp;&nbsp; 4% used </strong>
        [0x227d0000, 0x22aeb958, 0x22aeba00, 0x26bd0000)<br />
        &nbsp;compacting perm gen&nbsp; total 8192K, used 2898K [0x26bd0000, 0x273d0000, 0x2abd0000)<br />
        &nbsp;&nbsp; the space 8192K,&nbsp; 35% used [0x26bd0000, 0x26ea4ba8, 0x26ea4c00, 0x273d0000)<br />
        &nbsp;&nbsp;&nbsp; ro space 8192K,&nbsp; 66% used [0x2abd0000, 0x2b12bcc0, 0x2b12be00, 0x2b3d0000)<br />
        &nbsp;&nbsp;&nbsp; rw space 12288K,&nbsp; 46% used [0x2b3d0000, 0x2b972060, 0x2b972200, 0x2bfd0000)<br />
        }<br />
        , 0.0757599 secs]<br />
        </li>
        <li>
        <strong>-Xloggc:filename</strong>
        :与上面几个配合使用，把相关日志信息记录到文件以便分析。</li>
    </ul>
    </li>
    <li>
    <strong>常见配置汇总</strong>
    <ol>
        <li>堆设置
        <ul>
            <li>
            <strong>-Xms</strong>
            :初始堆大小  </li>
            <li>
            <strong>-Xmx</strong>
            :最大堆大小  </li>
            <li>
            <strong>-XX:NewSize=n</strong>
            :设置年轻代大小  </li>
            <li>
            <strong>-XX:NewRatio=n:</strong>
            设置年轻代和年老代的比值。如:为3，表示年轻代与年老代比值为1：3，年轻代占整个年轻代年老代和的1/4  </li>
            <li>
            <strong>-XX:SurvivorRatio=n</strong>
            :年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如：3，表示Eden：Survivor=3：2，一个Survivor区占整个年轻代的1/5  </li>
            <li>
            <strong>-XX:MaxPermSize=n</strong>
            :设置持久代大小</li>
        </ul>
        </li>
        <li>收集器设置
        <ul>
            <li>
            <strong>-XX:+UseSerialGC</strong>
            :设置串行收集器  </li>
            <li>
            <strong>-XX:+UseParallelGC</strong>
            :设置并行收集器  </li>
            <li>
            <strong>-XX:+UseParalledlOldGC</strong>
            :设置并行年老代收集器  </li>
            <li>
            <strong>-XX:+UseConcMarkSweepGC</strong>
            :设置并发收集器</li>
        </ul>
        </li>
        <li>垃圾回收统计信息
        <ul>
            <li>
            <strong>-XX:+PrintGC</strong>
            </li>
            <li>
            <strong>-XX:+PrintGCDetails</strong>
            </li>
            <li>
            <strong>-XX:+PrintGCTimeStamps</strong>
            </li>
            <li>
            <strong>-Xloggc:filename</strong>
            </li>
        </ul>
        </li>
        <li>并行收集器设置
        <ul>
            <li>
            <strong>-XX:ParallelGCThreads=n</strong>
            :设置并行收集器收集时使用的CPU数。并行收集线程数。  </li>
            <li>
            <strong>-XX:MaxGCPauseMillis=n</strong>
            :设置并行收集最大暂停时间  </li>
            <li>
            <strong>-XX:GCTimeRatio=n</strong>
            :设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)</li>
        </ul>
        </li>
        <li>并发收集器设置
        <ul>
            <li>
            <strong>-XX:+CMSIncrementalMode</strong>
            :设置为增量模式。适用于单CPU情况。  </li>
            <li>
            <strong>-XX:ParallelGCThreads=n</strong>
            :设置并发收集器年轻代收集方式为并行收集时，使用的CPU数。并行收集线程数。</li>
        </ul>
        </li>
    </ol>
    </li>
</ol>
<br />
<strong><span style="font-size: x-small;">四、调优总结</span>
</strong>
<ol>
    <li>
    <strong>年轻代大小选择</strong>
    <ul>
        <li>
        <strong>响应时间优先的应用</strong>
        ：<strong><span style="color: #0000ff;">尽可能设大，直到接近系统的最低响应时间限制</span>
        </strong>
        （根据实际情况选择）。在此种情况下，年轻代收集发生的频率也是最小的。同时，减少到达年老代的对象。  </li>
        <li>
        <strong>吞吐量优先的应用</strong>
        ：尽可能的设置大，可能到达Gbit的程度。因为对响应时间没有要求，垃圾收集可以并行进行，一般适合8CPU以上的应用。</li>
    </ul>
    </li>
    <li>
    <strong>年老代大小选择</strong>
    <ul>
        <li>
        <strong>响应时间优先的应用</strong>
        ：年老代使用并发收集器，所以其大小需要小心设置，一般要考虑<strong>并发会话率</strong>
        和<strong>会话持续时间</strong>
        等一些参数。如果堆设置小了，可以会造成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式；如果堆大了，则需要较长的收集时间。最优化的方案，一般需要参考以下数据获得：
        <ul>
            <li>并发垃圾收集信息  </li>
            <li>持久代并发收集次数  </li>
            <li>传统GC信息  </li>
            <li>花在年轻代和年老代回收上的时间比例</li>
        </ul>
        减少年轻代和年老代花费的时间，一般会提高应用的效率<br />
        </li>
        <li>
        <strong>吞吐量优先的应用</strong>
        ：一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代。原因是，这样可以尽可能回收掉大部分短期对象，减少中期的对象，而年老代尽存放长期存活对象。</li>
    </ul>
    </li>
    <li>
    <strong>较小堆引起的碎片问题<br />
    </strong>
    因
    为年老代的并发收集器使用标记、清除算法，所以不会对堆进行压缩。当收集器回收时，他会把相邻的空间进行合并，这样可以分配给较大的对象。但是，当堆空间
    较小时，运行一段时间以后，就会出现&#8220;碎片&#8221;，如果并发收集器找不到足够的空间，那么并发收集器将会停止，然后使用传统的标记、清除方式进行回收。如果出
    现&#8220;碎片&#8221;，可能需要进行如下配置：
    <ul>
        <li>
        <strong>-XX:+UseCMSCompactAtFullCollection</strong>
        ：使用并发收集器时，开启对年老代的压缩。  </li>
        <li>
        <strong>-XX:CMSFullGCsBeforeCompaction=0</strong>
        ：上面配置开启的情况下，这里设置多少次Full GC后，对年老代进行压缩</li>
    </ul>
    </li>
</ol>
</span>
<img src ="http://www.blogjava.net/conans/aggbug/339582.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/conans/" target="_blank">CONAN</a> 2010-12-02 10:53 <a href="http://www.blogjava.net/conans/articles/339582.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>慢慢琢磨JVM</title><link>http://www.blogjava.net/conans/articles/338994.html</link><dc:creator>CONAN</dc:creator><author>CONAN</author><pubDate>Thu, 25 Nov 2010 02:11:00 GMT</pubDate><guid>http://www.blogjava.net/conans/articles/338994.html</guid><description><![CDATA[转自：http://www.javaeye.com/topic/821872<br />
<h2>
1 JVM简介
</h2>
<p>JVM是我们Javaer的最基本功底了，刚开始学Java的时候，一般都是从&#8220;Hello World&#8221;开始的，然后会写个复杂点class，然后再找一些开源框架，比如Spring，Hibernate等等，再然后就开发企业级的应用，比如网站、企业内部应用、实时交易系统等等，直到某一天突然发现做的系统咋就这么慢呢，而且时不时还来个内存溢出什么的，今天是交易系统报了StackOverflowError，明天是网站系统报了个OutOfMemoryError，这种错误又很难重现，只有分析Javacore和dump文件，运气好点还能分析出个结果，运行遭的点，就直接去庙里烧香吧！每天接客户的电话都是战战兢兢的，生怕再出什么幺蛾子了。我想Java做的久一点的都有这样的经历，那这些问题的最终根结是在哪呢？—— JVM。</p>
<p>JVM全称是Java Virtual Machine，Java虚拟机，也就是在计算机上再虚拟一个计算机，这和我们使用 VMWare不一样，那个虚拟的东西你是可以看到的，这个JVM你是看不到的，它存在内存中。我们知道计算机的基本构成是：运算器、控制器、存储器、输入和输出设备，那这个JVM也是有这成套的元素，运算器是当然是交给硬件CPU还处理了，只是为了适应&#8220;一次编译，随处运行&#8221;的情况，需要做一个翻译动作，于是就用了JVM自己的命令集，这与汇编的命令集有点类似，每一种汇编命令集针对一个系列的CPU，比如8086系列的汇编也是可以用在8088上的，但是就不能跑在8051上，而JVM的命令集则是可以到处运行的，因为JVM做了翻译，根据不同的CPU，翻译成不同的机器语言。</p>
<p>JVM中我们最需要深入理解的就是它的存储部分，存储？硬盘？NO，NO， JVM是一个内存中的虚拟机，那它的存储就是内存了，我们写的所有类、常量、变量、方法都在内存中，这决定着我们程序运行的是否健壮、是否高效，接下来的部分就是重点介绍之。</p>
<h2>
2 JVM的组成部分
</h2>
<p>我们先把JVM这个虚拟机画出来，如下图所示：</p>
<p>
<br />
<img src="http://dl.javaeye.com/upload/attachment/353109/0c3fda2c-286b-3819-b47a-4daf15d3d08c.png" alt="" /></p>
<p>从这个图中可以看到，JVM是运行在操作系统之上的，它与硬件没有直接的交互。我们再来看下JVM有哪些组成部分，如下图所示：</p>
<p><br />
<img src="http://dl.javaeye.com/upload/attachment/353113/a3a81279-4006-3fa6-9b7f-e1ac95228efd.png" alt="" /><br />
&nbsp;该图参考了网上广为流传的JVM构成图，大家看这个图，整个JVM分为四部分：</p>
<p><span style="font-family: Wingdings;">q&nbsp;</span>Class Loader 类加载器</p>
<p>类加载器的作用是加载类文件到内存，比如编写一个HelloWord.java程序，然后通过javac编译成class文件，那怎么才能加载到内存中被执行呢？Class Loader承担的就是这个责任，那不可能随便建立一个.class文件就能被加载的，Class Loader加载的class文件是有格式要求，在《JVM Specification》中式这样定义Class文件的结构：</p>
<div style="padding: 4pt; margin-left: 0cm; margin-right: 28.65pt;">
<p style="margin-right: 0cm; text-indent: 17.5pt;"><span style="font-size: 10pt; color: windowtext;">&nbsp;&nbsp;&nbsp; ClassFile {</span></p>
<p style="margin-right: 0cm; text-indent: 17.5pt;"><span style="font-size: 10pt; color: windowtext;">&nbsp;&nbsp;&nbsp; &nbsp;u4
magic;</span></p>
<p style="margin-right: 0cm; text-indent: 17.5pt;"><span style="font-size: 10pt; color: windowtext;">&nbsp;&nbsp;&nbsp; &nbsp;u2
minor_version;</span></p>
<p style="margin-right: 0cm; text-indent: 17.5pt;"><span style="font-size: 10pt; color: windowtext;">&nbsp;&nbsp;&nbsp; &nbsp;u2 major_version;</span></p>
<p style="margin-right: 0cm; text-indent: 17.5pt;"><span style="font-size: 10pt; color: windowtext;">&nbsp;&nbsp;&nbsp; &nbsp;u2
constant_pool_count;</span></p>
<p style="margin-right: 0cm; text-indent: 17.5pt;"><span style="font-size: 10pt; color: windowtext;">&nbsp;&nbsp;&nbsp; &nbsp;cp_info
constant_pool[constant_pool_count-1];</span></p>
<p style="margin-right: 0cm; text-indent: 17.5pt;"><span style="font-size: 10pt; color: windowtext;">&nbsp;&nbsp;&nbsp; &nbsp;u2
access_flags;</span></p>
<p style="margin-right: 0cm; text-indent: 17.5pt;"><span style="font-size: 10pt; color: windowtext;">&nbsp;&nbsp;&nbsp; &nbsp;u2
this_class;</span></p>
<p style="margin-right: 0cm; text-indent: 17.5pt;"><span style="font-size: 10pt; color: windowtext;">&nbsp;&nbsp;&nbsp; &nbsp;u2
super_class;</span></p>
<p style="margin-right: 0cm; text-indent: 17.5pt;"><span style="font-size: 10pt; color: windowtext;">&nbsp;&nbsp;&nbsp; &nbsp;u2
interfaces_count;</span></p>
<p style="margin-right: 0cm; text-indent: 17.5pt;"><span style="font-size: 10pt; color: windowtext;">&nbsp;&nbsp;&nbsp; &nbsp;u2
interfaces[interfaces_count];</span></p>
<p style="margin-right: 0cm; text-indent: 17.5pt;"><span style="font-size: 10pt; color: windowtext;">&nbsp;&nbsp;&nbsp; &nbsp;u2
fields_count;</span></p>
<p style="margin-right: 0cm; text-indent: 17.5pt;"><span style="font-size: 10pt; color: windowtext;">&nbsp;&nbsp;&nbsp; &nbsp;field_info
fields[fields_count];</span></p>
<p style="margin-right: 0cm; text-indent: 17.5pt;"><span style="font-size: 10pt; color: windowtext;">&nbsp;&nbsp;&nbsp; &nbsp;u2
methods_count;</span></p>
<p style="margin-right: 0cm; text-indent: 17.5pt;"><span style="font-size: 10pt; color: windowtext;">&nbsp;&nbsp;&nbsp; &nbsp;method_info
methods[methods_count];</span></p>
<p style="margin-right: 0cm; text-indent: 17.5pt;"><span style="font-size: 10pt; color: windowtext;">&nbsp;&nbsp;&nbsp; &nbsp;u2
attributes_count;</span></p>
<p style="margin-right: 0cm; text-indent: 17.5pt;"><span style="font-size: 10pt; color: windowtext;">&nbsp;&nbsp;&nbsp; &nbsp;attribute_info
attributes[attributes_count];</span></p>
<p style="margin-right: 0cm; text-indent: 17.5pt;"><span style="font-size: 10pt; color: windowtext;">&nbsp;&nbsp;&nbsp; }</span></p>
</div>
<p>需要详细了解的话，可以仔细阅读《JVM Specification》的第四章&#8220;The class File Format&#8221;，这里不再详细说明。</p>
<p><span>友情提示：<span>Class
Loader</span>只管加载，只要符合文件结构就加载，至于说能不能运行，则不是它负责的，那是由<span>Execution
Engine</span>负责的。</span></p>
<p><span style="font-family: Wingdings;">q&nbsp;</span>Execution Engine 执行引擎</p>
<p>执行引擎也叫做解释器(Interpreter)，负责解释命令，提交操作系统执行。</p>
<p><span style="font-family: Wingdings;">q&nbsp;</span>Native Interface本地接口</p>
<p>本地接口的作用是融合不同的编程语言为Java所用，它的初衷是融合C/C++程序，Java诞生的时候是C/C++横行的时候，要想立足，必须有一个聪明的、睿智的调用C/C++程序，于是就在内存中专门开辟了一块区域处理标记为native的代码，它的具体做法是Native Method Stack中登记native方法，在Execution Engine执行时加载native libraies。目前该方法使用的是越来越少了，除非是与硬件有关的应用，比如通过Java程序驱动打印机，或者Java系统管理生产设备，在企业级应用中已经比较少见，因为现在的异构领域间的通信很发达，比如可以使用Socket通信，也可以使用Web Service等等，不多做介绍。</p>
<p><span style="font-family: Wingdings;">q&nbsp;</span>Runtime data area运行数据区</p>
<p>运行数据区是整个JVM的重点。我们所有写的程序都被加载到这里，之后才开始运行，Java生态系统如此的繁荣，得益于该区域的优良自治，下一章节详细介绍之。</p>
<p>整个JVM框架由加载器加载文件，然后执行器在内存中处理数据，需要与异构系统交互是可以通过本地接口进行，瞧，一个完整的系统诞生了！</p>
<h2>
2 JVM的内存管理
</h2>
<p>所有的数据和程序都是在运行数据区存放，它包括以下几部分：</p>
<p><span style="font-family: Wingdings;">q&nbsp;</span>Stack 栈</p>
<p>栈也叫栈内存，是Java程序的运行区，是在线程创建时创建，它的生命期是跟随线程的生命期，线程结束栈内存也就释放，对于栈来说不存在垃圾回收问题，只要线程一结束，该栈就Over。问题出来了：栈中存的是那些数据呢？又什么是格式呢？</p>
<p>栈中的数据都是以栈帧（Stack Frame）的格式存在，栈帧是一个内存区块，是一个数据集，是一个有关方法(Method)和运行期数据的数据集，当一个方法A被调用时就产生了一个栈帧F1，并被压入到栈中，A方法又调用了B方法，于是产生栈帧F2也被压入栈，执行完毕后，先弹出F2栈帧，再弹出F1栈帧，遵循&#8220;先进后出&#8221;原则。</p>
<p>那栈帧中到底存在着什么数据呢？栈帧中主要保存3类数据：本地变量（Local Variables），包括输入参数和输出参数以及方法内的变量；栈操作（Operand Stack），记录出栈、入栈的操作；栈帧数据（Frame Data），包括类文件、方法等等。光说比较枯燥，我们画个图来理解一下Java栈，如下图所示：</p>
<p><br />
<img src="http://dl.javaeye.com/upload/attachment/353115/183617ef-4489-3b4d-a3f0-ec628cd7241f.png" alt="" /><br />
&nbsp;图示在一个栈中有两个栈帧，栈帧2是最先被调用的方法，先入栈，然后方法2又调用了方法1，栈帧1处于栈顶的位置，栈帧2处于栈底，执行完毕后，依次弹出栈帧1和栈帧2，线程结束，栈释放。</p>
<p><span style="font-family: Wingdings;">q&nbsp;</span>Heap 堆内存</p>
<p>一个JVM实例只存在一个堆类存，堆内存的大小是可以调节的。类加载器读取了类文件后，需要把类、方法、常变量放到堆内存中，以方便执行器执行，堆内存分为三部分：</p>
<p style="text-indent: 21.1pt;"><strong>Permanent Space </strong><strong>永久存储区</strong></p>
<p>永久存储区是一个常驻内存区域，用于存放JDK自身所携带的Class,Interface的元数据，也就是说它存储的是运行环境必须的类信息，被装载进此区域的数据是不会被垃圾回收器回收掉的，关闭JVM才会释放此区域所占用的内存。</p>
<p style="text-indent: 21.1pt;"><strong>Young Generation Space </strong><strong>新生区</strong></p>
<p><span>新生区是类的诞生、成长、消亡的区域，一个类在这里产生，应用，最后被垃圾回收器收集，结束生命。新生区又分为两部分：
伊甸区（Eden space）和幸存者区（Survivor pace），所有的类都是在伊甸区被new出来的。幸存区有两个： 0区（<span>Survivor
0 space</span>）和1区（Survivor 1 space）。当伊甸园的空间用完时，程序又需要创建对象，JVM的垃圾回收器将对伊甸园区进行垃圾回收，将伊甸园区中的不再被其他对象所引用的对象进行销毁。然后将伊甸园中的剩余对象移动到幸存0区。若幸存0区也满了，再对该区进行垃圾回收，然后移动到1区。那如果1区也满了呢？再移动到养老区。</span></p>
<p style="text-indent: 21.1pt;"><strong>Tenure generation space</strong><strong>养老区</strong></p>
<p>养老区用于保存从新生区筛选出来的JAVA对象，一般池对象都在这个区域活跃。&nbsp;&nbsp; 三个区的示意图如下：</p>
<p><br />
<img src="http://dl.javaeye.com/upload/attachment/353117/e1476bc8-85c2-36ba-9e47-77d34361f097.jpg" alt="" /><br />
&nbsp;<span style="font-family: Wingdings;">q&nbsp;</span>Method Area 方法区</p>
<p>方法区是被所有线程共享，该区域保存所有字段和方法字节码，以及一些特殊方法如构造函数，接口代码也在此定义。</p>
<p><span style="font-family: Wingdings;">q&nbsp;</span>PC Register 程序计数器</p>
<p>每个线程都有一个程序计数器，就是一个指针，指向方法区中的方法字节码，由执行引擎读取下一条指令。</p>
<p><span style="font-family: Wingdings;">q&nbsp;</span>Native Method Stack 本地方法栈</p>
<h2>
3 JVM相关问题
</h2>
<p style="text-indent: 21.1pt;"><strong>问：堆和栈有什么区别</strong></p>
<p>答：堆是存放对象的，但是对象内的临时变量是存在栈内存中，如例子中的methodVar是在运行期存放到栈中的。</p>
<p>栈是跟随线程的，有线程就有栈，堆是跟随JVM的，有JVM就有堆内存。</p>
<p style="text-indent: 21.1pt;"><strong>问：堆内存中到底存在着什么东西？</strong></p>
<p>答：对象，包括对象变量以及对象方法。</p>
<p style="text-indent: 21.1pt;"><strong>问：类变量和实例变量有什么区别？</strong></p>
<p>答：静态变量是类变量，非静态变量是实例变量，直白的说，有static修饰的变量是静态变量，没有static修饰的变量是实例变量。静态变量存在方法区中，实例变量存在堆内存中。</p>
<p style="text-indent: 21.1pt;"><strong>问：我听说类变量是在JVM</strong><strong>启动时就初始化好的，和你这说的不同呀！</strong></p>
<p>答：那你是道听途说，信我的，没错。</p>
<p style="text-indent: 21.1pt;"><strong>问：Java</strong><strong>的方法（函数）到底是传值还是传址？</strong></p>
<p>答：都不是，是以传值的方式传递地址，具体的说原生数据类型传递的值，引用类型传递的地址。对于原始数据类型，JVM的处理方法是从Method Area或Heap中拷贝到Stack，然后运行frame中的方法，运行完毕后再把变量指拷贝回去。</p>
<p style="text-indent: 21.1pt;"><strong>问：为什么会产生OutOfMemory</strong><strong>产生？</strong></p>
<p>答：一句话：Heap内存中没有足够的可用内存了。这句话要好好理解，不是说Heap没有内存了，是说新申请内存的对象大于Heap空闲内存，比如现在Heap还空闲1M，但是新申请的内存需要1.1M，于是就会报OutOfMemory了，可能以后的对象申请的内存都只要0.9M，于是就只出现一次OutOfMemory，GC也正常了，看起来像偶发事件，就是这么回事。&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 但如果此时GC没有回收就会产生挂起情况，系统不响应了。</p>
<p style="text-indent: 21.1pt;"><strong>问：我产生的对象不多呀，为什么还会产生OutOfMemory</strong><strong>？</strong></p>
<p>答：你继承层次忒多了，Heap中
产生的对象是先产生
父类，然后才产生子类，明白不？</p>
<p style="text-indent: 21.1pt;"><strong>问：OutOfMemory</strong><strong>错误分几种？</strong></p>
<p>答：分两种，分别是&#8220;OutOfMemoryError:java heap size&#8221;和&#8221;OutOfMemoryError: PermGen space&#8221;，两种都是内存溢出，heap size是说申请不到新的内存了，这个很常见，检查应用或调整堆内存大小。</p>
<p>&#8220;PermGen space&#8221;是因为永久存储区满了，这个也很常见，一般在热发布的环境中出现，是因为每次发布应用系统都不重启，久而久之永久存储区中的死对象太多导致新对象无法申请内存，一般重新启动一下即可。</p>
<p style="text-indent: 21.1pt;"><strong>问：为什么会产生StackOverflowError</strong><strong>？</strong></p>
<p>答：因为一个线程把Stack内存全部耗尽了，一般是递归函数造成的。</p>
<p style="text-indent: 21.1pt;"><strong>问：一个机器上可以看多个JVM</strong><strong>吗？JVM</strong><strong>之间可以互访吗？</strong></p>
<p>答：可以多个JVM，只要机器承受得了。JVM之间是不可以互访，你不能在A-JVM中访问B-JVM的Heap内存，这是不可能的。在以前老版本的JVM中，会出现A-JVM Crack后影响到B-JVM，现在版本非常少见。</p>
<p style="text-indent: 21.1pt;"><strong>问：为什么Java</strong><strong>要采用垃圾回收机制，而不采用C/C++</strong><strong>的显式内存管理？</strong></p>
<p>答：为了简单，内存管理不是每个程序员都能折腾好的。</p>
<p style="text-indent: 21.1pt;"><strong>问：为什么你没有详细介绍垃圾回收机制？</strong></p>
<p>答：垃圾回收机制每个JVM都不同，JVM Specification只是定义了要自动释放内存，也就是说它只定义了垃圾回收的抽象方法，具体怎么实现各个厂商都不同，算法各异，这东西实在没必要深入。</p>
<p style="text-indent: 21.1pt;"><strong>问：JVM</strong><strong>中到底哪些区域是共享的？哪些是私有的？</strong></p>
<p>答：Heap和Method Area是共享的，其他都是私有的，</p>
<p style="text-indent: 21.1pt;"><strong>问：什么是JIT</strong><strong>，你怎么没说？</strong></p>
<p>答：JIT是指Just In Time，有的文档把JIT作为JVM的一个部件来介绍，有的是作为执行引擎的一部分来介绍，这都能理解。Java刚诞生的时候是一个解释性语言，别嘘，即使编译成了字节码（byte code）也是针对JVM的，它需要再次翻译成原生代码(native code)才能被机器执行，于是效率的担忧就提出来了。Sun为了解决该问题提出了一套新的机制，好，你想编译成原生代码，没问题，我在JVM上提供一个工具，把字节码编译成原生码，下次你来访问的时候直接访问原生码就成了，于是JIT就诞生了，就这么回事。</p>
<p style="text-indent: 21.1pt;"><strong>问：JVM</strong><strong>还有哪些部分是你没有提到的？</strong></p>
<p>答：JVM是一个异常复杂的东西，写一本砖头书都不为过，还有几个要说明的：</p>
<p>常量池（constant pool）：按照顺序存放程序中的常量，并且进行索引编号的区域。比如<span>int i
=100</span>，这个100就放在常量池中。</p>
<p>安全管理器（Security Manager）：提供Java运行期的安全控制，防止恶意攻击，比如指定读取文件，写入文件权限，网络访问，创建进程等等，Class Loader在Security Manager认证通过后才能加载class文件的。</p>
<p>方法索引表（<span>Methods
table</span>），记录的是每个method的地址信息，Stack和Heap中的地址指针其实是指向Methods table地址。</p>
<p style="text-indent: 21.1pt;"><strong>问：为什么不建议在程序中显式的生命System.gc()</strong><strong>？</strong></p>
<p>答：因为显式声明是做堆内存全扫描，也就是Full GC，是需要停止所有的活动的（Stop &nbsp;The World Collection），你的应用能承受这个吗？</p>
<p style="text-indent: 21.1pt;"><strong>问：JVM</strong><strong>有哪些调整参数？</strong></p>
<p>答：非常多，自己去找，堆内存、栈内存的大小都可以定义，甚至是堆内存的三个部分、新生代的各个比例都能调整。</p>
<br />
<img src ="http://www.blogjava.net/conans/aggbug/338994.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/conans/" target="_blank">CONAN</a> 2010-11-25 10:11 <a href="http://www.blogjava.net/conans/articles/338994.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Sun JDK OOM</title><link>http://www.blogjava.net/conans/articles/329031.html</link><dc:creator>CONAN</dc:creator><author>CONAN</author><pubDate>Mon, 16 Aug 2010 10:21:00 GMT</pubDate><guid>http://www.blogjava.net/conans/articles/329031.html</guid><description><![CDATA[转自：http://blog.bluedavy.com/?p=91<br />
<br />
<div>
<p>Java的自动内存管理机制给开发人员带来了很多的便利，在设计、开发时可以完全不用考虑要
分配多少内存，要记得回收内存等，但同时也带来了各种各样的问题，其中最典型的问题就是OOM，大部分Java开发人员估计都看到过
java.lang.OutOfMemoryError这样的错误信息，在这篇文章中，就来介绍下Sun
JDK中有哪几种OOM、OOM示例、造成OOM的原因的查找、解决以及Sun JDK代码中处理OOM的方式。</p>
<p>PDF版本请从此下载：<a href="http://blog.bluedavy.com/open/Sun-JDK-OOM.pdf">http://blog.bluedavy.com/open/Sun-JDK-OOM.pdf</a></p>
<p>1	OOM的种类<br />
在Sun JDK中运行时，Java程序有可能出现如下几种OOM错误：<br />
	java.lang.OutOfMemoryError: unable to create new native thread<br />
当调用new
Thread时，如已创建不了线程了，则会抛出此错误，如果是JDK内部必须创建成功的线程，那么会造成Java进程退出，如果是用户线程，则仅抛出
OOM，创建不了的原因通常是创建了太多线程，耗尽了内存，通常可通过减少创建的线程数，或通过-Xss调小线程所占用的栈大小来减少对Java
对外内存的消耗。</p>
<p>	java.lang.OutOfMemoryError: request <size> bytes for <reason>. Out of swap space?<br />
当JNI模块或JVM内部进行malloc操作（例如GC时做mark）时，需要消耗堆外的内存，如此时Java进程所占用的地址空间超过限制（例如
windows: 2G，linux: 3G），或物理内存、swap区均使用完毕，那么则会出现此错误，当出现此错误时，Java进程将会退出。</reason></size></p>
<p>	java.lang.OutOfMemoryError: Java heap space<br />
这是最常见的OOM错误，当通过new创建对象或数组时，如Java Heap空间不足（新生代不足，触发minor GC，还是不够，触发Full GC，还是不够），则抛出此错误。</p>
<p>	java.lang.OutOfMemoryError: GC overhead limit execeeded<br />
当通过new创建对象或数组时，如Java
Heap空间不足，且GC所使用的时间占了程序总时间的98%，且Heap剩余空间小于2%，则抛出此错误，以避免Full
GC一直执行，可通过UseGCOverheadLimit来决定是否开启这种策略，可通过GCTimeLimit和GCHeapFreeLimit来控
制百分比。</p>
<p>	java.lang.OutOfMemoryError: PermGen space<br />
当加载class时，在进行了Full GC后如PermGen空间仍然不足，则抛出此错误。<br />
对于以上几种OOM错误，其中容易造成严重后果的是Out of swap space这种，因为这种会造成Java进程退出，而其他几种只要不是在main线程抛出的，就不会造成Java进程退出。</p>
<p>2	OOM示例、原因查找和解决<br />
这些示例的class以及源码请从<a href="http://blog.bluedavy.com/jvm/cases/oom/OOMCases.zip">http://blog.bluedavy.com/jvm/cases/oom/OOMCases.zip</a>下载，建议在运行前不要看源码，毕竟源码是简单的例子，如果直接看源码的话，可能会少了查找原因的乐趣。</p>
<p>当Java程序运行时，会有很多种造成OOM的现象，这里面有些会比较容易查找出原因，而有些会非常困难，下面是来看一些OOM的Example。<br />
	Example 1<br />
以-Xms20m -Xmx20m -Xmn10m -XX:+UseParallelGC参数运行com.bluedavy.oom.JavaHeapSpaceCase1<br />
运行后在输出的日志中可看到大量的Full GC信息，以及：<br />
java.lang.OutOfMemoryError: GC overhead limit exceeded<br />
和java.lang.OutOfMemoryError: Java heap space<br />
根据上面所说的OOM种类，可以知道这是在new对象或数组时Java Heap Space不足造成的，对于这种OOM，需要知道的是程序中哪些部分占用了Java Heap。<br />
要知道程序中哪些部分占用了Java Heap，首先必须拿到Java Heap中的信息，尤其是OOM时的内存信息，在Sun
JDK中可通过在启动参数上加上-XX:+ HeapDumpOnOutOfMemoryError来获得OOM时的Java
Heap中的信息，当出现OOM时，会自动生成一个java_pid[pid].hprof的文件。<br />
于是在启动参数上加上上面的参数，再次运行JavaHeapSpaceCase1，可看到在当前运行的路径下生成了一个
java_pid10852.hprof（由于pid不同，你看到的可能是不一样的文件名）的文件，在拿到这个文件后，就可通过
mat（http://www.eclipse.org/mat/）来进行分析了。<br />
用mat打开上面的文件（默认情况下mat认为heap dump文件应以.bin结尾，因此请改名或以open
file方式打开），打开后点击dominator_tree，可看到sun.misc.Launcher$AppClassLoader占据了大部分的
内存，继续点开看，则可看到是由于com.bluedavy.oom.Caches中有一个ArrayList，其中存放的对象占据了大部分的内存，因此
解决这个OOM的办法是，让Caches类中放的对象总大小是有限制的，或者限制放入Caches的ArrayList中的对象个数。<br />
这种情况造成的OOM，在实际的场景中当使用缓存时很容易产生，对于所有的缓存、集合大小都应给定限制的最大大小，以避免出现缓存或集合太大，导致消耗了过多的内存，从而导致OOM。</p>
<p>	Example 2<br />
以-Xms20m -Xmx20m -Xmn10m -XX:+HeapDumpOnOutOfMemoryError执行com.bluedavy.oom.JavaHeapSpaceCase2<br />
运行后在输出的日志中可看到大量的Full GC和java.lang.OutOfMemoryError: Java heap space。<br />
同样，首先用mat打开需要分析的hprof文件，很惊讶的发现什么都看不出来，Java Heap
Space还很充足，这就奇怪了，还好除了能在OOM时自动dump出Heap的信息外，还可通过jmap命令手工dump，于是，在运行期出现频繁
Full GC、OOM的时候，手工通过jmap &#8211;dump:file=heap.bin,format=b
[pid]生成一个heap.bin文件，把这个heap.bin文件也拿到mat中分析，杯具，还是什么都看不出来，还好，还有一招，就是直接用
jmap
&#8211;histo看看到底什么对象占用了大多数的内存，执行一次，看到[I占用了最多的内存，没用，因为没法知道这个[I到底是代码中哪个部分创建的，不甘
心，多执行几次，很幸运，突然看到com.bluedavy.oom.Case2Object占据了最大的内存，于是查找代码中哪些地方创建了这个对象，
发现了代码中有一个线程创建了大量的Case2Object，修改即可。<br />
从这个例子中，可以看到，在分析OOM问题时，一方面是依赖OOM时dump出来的文件，但这个文件其实只会在Java进程中第一次出现OOM时生成，之
后再OOM就不会生成了，这有可能出现真实的OOM的原因被假的OOM原因给掩盖掉；另一方面是依赖在出现OOM时的人工操作，这种人肉方式其实比较杯
具，因为只能先等到频繁Full GC、OOM，首先通过jmap
&#8211;histo来看看到底什么对象占用了大部分的内存（需要多执行几次，以确保正确性），上面的例子比较幸运，因为刚好是自定义的对象，如果是原生的类型，
那就只能借助dump文件来分析了，通过jmap
&#8211;dump手工dump出Heap文件，然后用MAT分析，但有可能会出现上面例子中的状况，就是mat分析上也看不出什么，顶多只能看到
unreachable objects里某些对象占用了大部分的内存，而通常情况看到的可能都是原生类型，一旦真的碰到jmap
&#8211;histo看到的是原生类型占用较多，jmap dump看到的是Java Heap
Space也不满的话，那只能说杯具了，在这种情况下，唯一能做的是捕捉所有的异常，然后打印，从而判断OOM是在哪行代码抛出的。</p>
<p>	Example 3<br />
以-Xms20m -Xmx20m -Xmn10m -XX:+HeapDumpOnOutOfMemoryError执行com.bluedavy.oom.JavaHeapSpaceCase3<br />
在控制台中可看到大量的java.lang.OutOfMemoryError: Java heap
space，把生成的hprof文件放入MAT中进行分析，还好看到确实是Java Heap Space满了，这就好办了，点开Dominator
Tree视图，可看到有一堆的线程，每个都占用了一定的内存，从而导致了OOM，要解决这个例子中的OOM，有四种办法：一是减少处理的线程数；二是处理
线程需要消耗的内存；三是提升线程的处理速度；四是增加机器，减少单台机器所需承担的请求量。<br />
上面这种状况在系统处理慢的时候比较容易出现。</p>
<p>	Example 4<br />
以-Xms20m -Xmx20m -Xmn10m -XX:+HeapDumpOnOutOfMemoryError执行com.bluedavy.oom.JavaHeapSpaceCase4<br />
在控制台可看到大量的java.lang.OutOfMemoryError: Java heap
space，把生成的hprof文件放入MAT中进行分析，可看到TaskExecutor中的handlers占据了大量的内存，分析代码，发现是由于
在task处理完后没有清除掉对应的handler造成的，修改即可解决此OOM问题。<br />
这是个典型的内存泄露的例子，如果不小心持有了本应释放引用的对象，那么就会造成内存泄露，这是在编写Java程序时需要特别注意的地方。</p>
<p>	Example 5<br />
以-Xms1536m -Xmx1536m -Xss100m执行com.bluedavy.oom.CannotCreateThreadCase<br />
在控制台可看到java.lang.OutOfMemoryError: unable to create new native thread。<br />
对于这种情况，一需要查看目前-Xss的大小，例如在这个例子中-Xss太大，导致连20个线程都无法创建，因此可解决的方法是降低-Xss的大小；如果
Xss使用的是默认值，那么可通过jstack来查看下目前Java进程中是不是创建了过多的线程，或者是java
heap太大，导致os没剩多少内存，从而创建不出线程。</p>
<p>	Example 6<br />
Out of swap的例子实在太难举了，就没在此列出了，对于Out of swap的OOM，需要首先观察是否因为Java
Heap设置太大，导致物理内存+swap区不够用；如果不是的话，则需关注到底是哪些地方占用了堆外的内存，可通过google-perftools来
跟踪是哪些代码在调用malloc，以及所耗的内存比例，在跟踪到后可继续查找原因。<br />
总体来说，Out of swap通常是最难查的OOM，由于其是直接退出java进程的，因此需要结合core
dump文件和hs_err_pid[pid].log进行分析，最关键的还是像查java heap那样，要查出到底是什么代码造成了native
heap的消耗。</p>
<p>	Example 7<br />
PermGen空间满造成OOM的情况通常采取的解决方法是简单的扩大PermSize。</p>
<p>总结上面的例子来看，对于OOM的情况，最重要的是根据OOM的种类查找到底是代码中的什么部分造成的消耗。</p>
<p>对于Java Heap Space OOM和GC overhead limit exceeded这两种类型，可通过heap
dump文件以及jmap &#8211;histo来进行分析，多数情况下可通过heap dump分析出原因，但也有少数情况会出现heap
dump分析不出原因，而jmap
&#8211;histo看到某原生类型占据了大部分的内存，这种情况就非常复杂了，只能是仔细查看代码，并捕捉OutOfMemoryError，从而来逐渐定位到
代码中哪部分抛出的。</p>
<p>对于Out of swap这种类型，其主要是地址空间超过了限制或者对外内存不够用了造成的，首先需要查看Java Heap设置是否过大，然后可结合google-perftools来查看到底是哪些代码调用了malloc，在堆外分配内存。</p>
<p>3	Sun JDK代码中处理OOM的方式<br />
在Sun JDK代码中，在不同的OOM时，会调用不同的处理方式来进行处理，下面就来看看JDK中几个典型的处理OOM的代码。<br />
	创建线程失败<br />
compiler_thread = new CompilerThread(queue, counters);<br />
if (compiler_thread == NULL || compiler_thread-&gt;osthread() == NULL){<br />
vm_exit_during_initialization(&#8220;java.lang.OutOfMemoryError&#8221;,<br />
&#8220;unable to create new native thread&#8221;);<br />
}<br />
对于JDK中必须创建成功的线程，如失败会通过调用vm_exit_during_initialization打印出OOM错误，并退出Java进程。<br />
对于非必须创建成功的线程，通常会调用<br />
THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(),<br />
&#8220;unable to create new native thread&#8221;);<br />
抛出OOM错误信息。</p>
<p>	调用os:malloc失败<br />
void *p =  os::malloc(bytes);<br />
if (p == NULL)<br />
vm_exit_out_of_memory(bytes, &#8220;Chunk::new&#8221;);<br />
return p;<br />
当os:malloc或os:commit_memory失败时，会直接输出错误信息，并退出Java进程。</p>
<p>	Java Heap上分配失败后<br />
report_java_out_of_memory(&#8220;Java heap space&#8221;);<br />
调用这个就表明了不会退出vm，而只是抛出OOM错误。<br />
例如PermGen分配失败时的代码更为直观：<br />
HeapWord* result = Universe::heap()-&gt;permanent_mem_allocate(size);<br />
if (result != NULL) {<br />
NOT_PRODUCT(Universe::heap()-&gt;<br />
check_for_non_bad_heap_word_value(result, size));<br />
assert(!HAS_PENDING_EXCEPTION,<br />
&#8220;Unexpected exception, will result in uninitialized storage&#8221;);<br />
return result;<br />
}<br />
// -XX:+HeapDumpOnOutOfMemoryError and -XX:OnOutOfMemoryError support<br />
report_java_out_of_memory(&#8220;PermGen space&#8221;);</p>
<p>
总体而言，对于一个大型系统而言，通常OOM是难以避免的现象，最重要的还是一旦出现OOM，要掌握排查的方法，另外就是，随着现在内存越来越便
宜，CMS GC越来越成熟，采用64
bit操作系统，开启大内存也是一种可选方式，基本上可以避免内存成为大问题，毕竟在Java中完全可能随便写几行代码就不知不觉消耗了很多内存。</p>
<p>ps: 感兴趣的同学还可参考sun官方的这篇关于OOM的文章：<br />
<a href="http://java.sun.com/javase/6/webnotes/trouble/TSG-VM/html/memleaks.html">http://java.sun.com/javase/6/webnotes/trouble/TSG-VM/html/memleaks.html</a></p>
</div>
<br />
<br />
<img src ="http://www.blogjava.net/conans/aggbug/329031.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/conans/" target="_blank">CONAN</a> 2010-08-16 18:21 <a href="http://www.blogjava.net/conans/articles/329031.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>