<?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-jacky Blog-文章分类-java</title><link>http://www.blogjava.net/jacky/category/6465.html</link><description>技术创造个人价值
Technology Creates The Values Of Person</description><language>zh-cn</language><lastBuildDate>Wed, 28 Feb 2007 02:47:28 GMT</lastBuildDate><pubDate>Wed, 28 Feb 2007 02:47:28 GMT</pubDate><ttl>60</ttl><item><title>Maximizing Java Performance on AIX: Part 5 - References and Conclusion[转]</title><link>http://www.blogjava.net/jacky/articles/25875.html</link><dc:creator>jacky</dc:creator><author>jacky</author><pubDate>Thu, 29 Dec 2005 06:04:00 GMT</pubDate><guid>http://www.blogjava.net/jacky/articles/25875.html</guid><wfw:comment>http://www.blogjava.net/jacky/comments/25875.html</wfw:comment><comments>http://www.blogjava.net/jacky/articles/25875.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jacky/comments/commentRss/25875.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jacky/services/trackbacks/25875.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width="100%">
<H1><SPAN style="COLOR: #999999">Maximizing Java Performance on AIX: </SPAN>Part 5 - References and Conclusion</H1><IMG class=display-img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=1></TD>
<TD class=no-print width=192><IMG height=18 alt=developerWorks src="http://www-128.ibm.com/developerworks/i/dw.gif" width=192></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD width="100%">
<TABLE class=no-print cellSpacing=0 cellPadding=0 width=160 align=right border=0>
<TBODY>
<TR>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD>
<TABLE cellSpacing=0 cellPadding=0 width=150 border=0>
<TBODY>
<TR>
<TD class=v14-header-1-small>Document options</TD></TR></TBODY></TABLE>
<TABLE class=v14-gray-table-border cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=no-padding width=150>
<TABLE cellSpacing=0 cellPadding=0 width=143 border=0>
<SCRIPT language=JavaScript type=text/javascript>
<!--
document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt=""/></td><td width="16"><img alt="Set printer orientation to landscape mode" height="16" src="//www.ibm.com/i/v14/icons/printer.gif" width="16" vspace="3" /></td><td width="122"><p><b><a class="smallplainlink" href="javascript:print()">Print this page</a></b></p></td></tr>');
//-->
</SCRIPT>

<TBODY>
<TR vAlign=top>
<TD width=8><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></TD>
<TD width=16><IMG height=16 alt="Set printer orientation to landscape mode" src="http://www.ibm.com/i/v14/icons/printer.gif" width=16 vspace=3></TD>
<TD width=122>
<P><B><A class=smallplainlink href="javascript:print()"><FONT color=#5c81a7 size=1>Print this page</FONT></A></B></P></TD></TR><NOSCRIPT></NOSCRIPT>
<SCRIPT language=JavaScript type=text/javascript>
<!--
document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt=""/></td><td width="16"><img src="//www.ibm.com/i/v14/icons/em.gif" height="16" width="16" vspace="3" alt="Email this page" /></td><td width="122"><p><a class="smallplainlink" href="javascript:void newWindow()"><b>E-mail this page</b></a></p></td></tr>');
//-->
</SCRIPT>

<TR vAlign=top>
<TD width=8><FONT color=#5c81a7 size=1><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></FONT></TD>
<TD width=16><FONT color=#5c81a7 size=1><IMG height=16 alt="Email this page" src="http://www.ibm.com/i/v14/icons/em.gif" width=16 vspace=3></FONT></TD>
<TD width=122>
<P><A class=smallplainlink href="javascript:void newWindow()"><B><FONT color=#5c81a7 size=1>E-mail this page</FONT></B></A></P></TD></TR><NOSCRIPT><tr valign="top"><td width="8"><img alt="" height="1" width="8" src="//www.ibm.com/i/c.gif"/></td><td width="16"><img alt="" width="16" height="16" src="//www.ibm.com/i/c.gif"/></td><td class="small" width="122"><p><span class="ast">Document options requiring JavaScript are not displayed</span></p></td></tr></NOSCRIPT></TBODY></TABLE></TD></TR></TBODY></TABLE><!--START RESERVED FOR FUTURE USE INCLUDE FILES--><BR>
<TABLE cellSpacing=0 cellPadding=0 width=150 border=0>
<TBODY>
<TR>
<TD class=v14-header-2-small>New site feature</TD></TR></TBODY></TABLE>
<TABLE class=v14-gray-table-border cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=no-padding width=150>
<TABLE cellSpacing=0 cellPadding=0 width=143 border=0>
<TBODY>
<TR vAlign=top>
<TD width=8><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></TD>
<TD><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/fw_bold.gif" width=16 vspace=3 border=0></TD>
<TD width=125>
<P><A class=smallplainlink href="http://www-128.ibm.com/developerworks/architecture"><FONT color=#5c81a7 size=1>Plan and design IT solutions with our new Architecture area</FONT></A> </P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><!--END RESERVED FOR FUTURE USE INCLUDE FILES--><BR>
<TABLE cellSpacing=0 cellPadding=0 width=150 border=0>
<TBODY>
<TR>
<TD class=v14-header-2-small>Rate this page</TD></TR></TBODY></TABLE>
<TABLE class=v14-gray-table-border cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=no-padding width=150>
<TABLE cellSpacing=0 cellPadding=0 width=143 border=0>
<TBODY>
<TR vAlign=top>
<TD width=8><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></TD>
<TD><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/d_bold.gif" width=16 vspace=3 border=0></TD>
<TD width=125>
<P><A class=smallplainlink href="http://www-128.ibm.com/developerworks/eserver/library/es-Javaperf5.html#rate"><B><FONT color=#996699 size=1>Help us improve this content</FONT></B></A></P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR></TD></TR></TBODY></TABLE>
<P>Level: Intermediate</P>
<P><A href="http://www-128.ibm.com/developerworks/eserver/library/es-Javaperf5.html#author"><FONT color=#996699>Amit Mathur</FONT></A> (<A href="mailto:amitmat@us.ibm.com?subject=Part 5  -  References and Conclusion"><FONT color=#5c81a7>amitmat@us.ibm.com</FONT></A>), Senior Technical Consultant and Solutions Enablement Manager, IBM<BR><A href="http://www-128.ibm.com/developerworks/eserver/library/es-Javaperf5.html#author"><FONT color=#996699>Sumit Chawla</FONT></A> (<A href="mailto:sumitc@us.ibm.com?subject=Part 5  -  References and Conclusion"><FONT color=#5c81a7>sumitc@us.ibm.com</FONT></A>), IBM Certified IT Architect and Technical Lead, Java Enablement, IBM<BR></P>
<P>17 May 2004</P>
<BLOCKQUOTE>This is the conclusion of the 5-part series providing tips and techniques that are commonly used for tuning Java™ applications for optimum performance on AIX®. We touch upon other interesting areas of Java performance tuning for AIX, look at a few case studies, and then end the series with a list of useful references. </BLOCKQUOTE><!--START RESERVED FOR FUTURE USE INCLUDE FILES-->
<SCRIPT language=JavaScript type=text/javascript>
<!--
if (document.referrer&&document.referrer!="") { 
   // document.write(document.referrer);
   var q = document.referrer;
   var engine = q;
   var isG = engine.search(/google\.com/i);
   var searchTerms;
   //var searchTermsForDisplay;
   if (isG != -1) { 
	   var i = q.search(/q=/);
	   var q2 = q.substring(i+2);
	   var j = q2.search(/&/);
	   j = (j == -1)?q2.length:j;
	   searchTerms = q.substring(i+2,i+2+j);
	   if (searchTerms.length != 0) {
	       searchQuery(searchTerms);
	       document.write("<div id=\"contents\"></div>");
	   }
   } 
}
//-->
</SCRIPT>
<!--END RESERVED FOR FUTURE USE INCLUDE FILES-->
<P><A name=N1005F><SPAN class=atitle><FONT face=Arial size=4>Introduction</FONT></SPAN></A></P>
<P>This is the conclusion of the 5-part series on Java Performance.The first article in the series laid the foundation for performance tuning, and parts 2, 3 and 4 looked at various bottlenecks that can affect a system's scalability and throughput. This article covers two important topics that were not covered previously, along with providing case studies and references.</P>
<P>A frequently asked question (FAQ) is about translating Sun-specific command-line switches to IBM-specific switches. Also, any serious performance tuning exercise, like benchmarks, cannot ignore system-wide tuning. We touch upon these topics briefly in the next section.</P>
<P>This is followed by a few case studies that attempt to illustrate how the tools and tips described in the series are applied to solve problems in the field. The emphasis is on understanding and learning how to use the tools and techniques mentioned in the series.</P>
<P>The article, and the series, concludes with a recap of useful references.</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/eserver/library/es-Javaperf5.html#main"><B><FONT color=#996699 size=2>Back to top</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=N10071><SPAN class=atitle><FONT face=Arial size=4>Other Important Considerations</FONT></SPAN></A></P>
<P>This section talks about translating Sun Java configuration to IBM Java configuration, and System-wide tuning for AIX applications. The scope of both of these topics is quite vast, so we only touch them briefly.</P>
<P><A name=N1007A><SPAN class=smalltitle><STRONG><FONT face=Arial>Translating Sun Java Switches</FONT></STRONG></SPAN></A></P>
<P>If you have an application that has been tuned for Sun Java, and you are attempting to migrate your application to AIX (or, for that matter, any platform running IBM Java), you may already have done the hard work. Understanding the application characteristics is half the battle. You can use the characteristics-based tuning tips explained in <A href="http://www.ibm.com/developerworks/eserver/library/es-Javaperf2.html"><FONT color=#5c81a7>Part 2</FONT></A> and <A href="http://www.ibm.com/developerworks/eserver/library/es-Javaperf3.html"><FONT color=#5c81a7>Part 3</FONT></A> based on the understanding obtained by the tuning exercise with Sun Java.</P>
<P>However, we frequently receive queries about how to translate specific Sun Java command-line switches to equivalent IBM Java command-line switches. These switches almost always correspond to Garbage Collection, as a well-tuned GC is essential to any Java-based application's performance. A mapping between Sun and IBM switches is difficult because of the difference in JVM architecture. The IBM Java does not contain a Generational Garbage Collector, and does not understand any command-line switches that start with <CODE>-XX</CODE>. IBM Java "Sovereign" architecture is not based on Sun HotSpot architecture as well. The easiest, and in most cases the quickest, way is to throw away all Sun-specific settings when running your application on IBM Platforms, and carrying out the fine-tuning as needed. But if you are curious about how some Sun switches map to IBM switches, read on.</P>
<P>The table below attempts to translate Sun Java GC command-line switches to equivalent IBM switches. This mapping is based on the functionality of Sun switches as described in the Sun-specific article "<A href="http://java.sun.com/docs/hotspot/gc1.4.2/" target=new><FONT color=#996699>Tuning Garbage Collection with the 1.4.2 Java Virtual Machine</FONT></A>". You should attempt to use this table only for the very specific purpose of locating an equivalent (or close) switch for IBM Java. This is not meant to replace the tuning exercise, as even heap size requirements can be quite different. For general GC tuning tips with IBM Java, as well as for information on these and other GC switches for IBM Java, please refer to Fine-tuning Java garbage collection performance. The creation of this table did not involve any performance-related testing, but was based entirely on the documented use of the Sun switches in the reference quoted above.</P>
<TABLE width=600 border=1>
<TBODY>
<TR>
<TD><B>Sun Switch</B> </TD>
<TD><B>Equivalent IBM Switch</B> </TD>
<TD><B>Notes</B> </TD></TR>
<TR>
<TD>-Xms, -Xmx</TD>
<TD>-Xms, -Xmx</TD>
<TD>These parameters, and their meaning, remain unchanged. You may still need to do heap sizing.</TD></TR>
<TR>
<TD>-XX:SurvivorRatio, -XX:NewSize, <BR>-XX:MaxNewSize, -XX:NewRatio</TD>
<TD>None</TD>
<TD>These switches can simply be removed, as they are for generational GC which doesn't apply for IBM Java.</TD></TR>
<TR>
<TD>-XX:MinHeapFreeRatio, -XX:MaxHeapFreeRatio</TD>
<TD>-Xminf, -Xmaxf</TD>
<TD>Heap expansion/shrinkage is controlled by other factors, not just these switches.</TD></TR>
<TR>
<TD>-Xverbose:gc, -XX:+PrintGCDetails</TD>
<TD>-Xverbose:gc</TD>
<TD>IBM Java verbosegc trace format is quite different from the Sun GC. More detailed tracing can be enabled as needed, but in most cases the default verbosegc traces are sufficient.</TD></TR>
<TR>
<TD>-XX:+UseParallelGC, -Xincgc, -XX:+AggressiveHeap</TD>
<TD>None</TD>
<TD>These are various types of Garbage Collectors supported by Sun. These do not apply to IBM Java.</TD></TR>
<TR>
<TD>-XX:+UseConcMarkSweepGC -XX:+UseParNewGC</TD>
<TD>-Xgcpolicy:optavgpause</TD>
<TD>Concurrent Low-pause collector is close to the IBM Concurrent Mark in intent (but not necessarily in design).</TD></TR>
<TR>
<TD>-XX:+CMSParallelRemarkEnabled</TD>
<TD>None</TD>
<TD>Not applicable for IBM Java.</TD></TR>
<TR>
<TD>-XX:ParallelGCThreads</TD>
<TD>-Xgcthreads</TD>
<TD>It is not advisable to change this setting at least for IBM Java.</TD></TR>
<TR>
<TD>-Dsun.rmi.dgc.client.gcInterval, -Dsun.rmi.dgc.server.gcInterval</TD>
<TD>-Dsun.rmi.dgc.client.gcInterval, -Dsun.rmi.dgc.server.gcInterval</TD>
<TD>See <A href="http://www.ibm.com/developerworks/eserver/library/es-Javaperf4.html#NI003"><FONT color=#5c81a7>NIO003 in Part 4.</FONT></A> </TD></TR></TBODY></TABLE>
<P>As the above table demonstrates, translating the switches in most cases will involve simply discarding the Sun switches for IBM platforms. This makes the GC tuning exercise for IBM Java quite painless, while still providing superior performance.</P>
<P><A name=N1012B><SPAN class=smalltitle><STRONG><FONT face=Arial>System-wide tuning</FONT></STRONG></SPAN></A></P>
<P>Using AIX tools like <CODE>schedo</CODE> and <CODE>vmo</CODE> has a system-wide effect, so a thorough coverage of these tools is beyond the scope of the current series. See the <A href="http://www-128.ibm.com/developerworks/eserver/library/es-Javaperf5.html#resources"><FONT color=#996699>Resources</FONT></A> section for more information on these tools. </P>
<P>But for most multi-tiered applications and especially benchmarks, system-wide tuning is unavoidable. There are several excellent resources available for AIX performance tuning that you can consider. To get an idea of the kind of tuning normally required, you can look at actual published benchmarks. If you look at a recent SpecJBB 2000 result for IBM Java on AIX, say <A href="http://www.spec.org/osg/jbb2000/results/res2003q3/jbb2000-20030624-00194.html" target=new><FONT color=#5c81a7>http://www.spec.org/osg/jbb2000/results/res2003q3/jbb2000-20030624-00194.html</FONT></A>, the OS tunings are mentioned below: 
<TABLE width=400 border=1>
<TBODY>
<TR>
<TD>Operating system tunings 
<UL>
<LI>SPINLOOPTIME=2000 
<LI>vmo -r -o lgpg_regions=256 -o lgpg_size=16777216 
<LI>setsched -S rr -P 40 -p $$ 
<LI>schedtune -t 400 -F 1 
<LI>vmtune -S 1 </LI></UL></TD></TR></TBODY></TABLE></P>
<P><B>Warning</B>: These settings must not be applied without carefully understanding the consequences, as an improper use of these settings can actually worsen the system performance.</P>
<P>So what do the above settings do? Referring to <A href="http://publib16.boulder.ibm.com/pseries/en_US/infocenter/base/aix52.htm" target=new><FONT color=#5c81a7>AIX documentation</FONT></A>, you can quickly get a better understanding of what each of these settings is doing. Let us examine each of these in turn.</P>
<P>The <CODE>SPINLOOPTIME</CODE> controls the number of times the system will retry a busy lock before yielding to another process. Since the default is 40, a higher value tells the system that it should try a little bit longer for the lock to be freed up. On multiprocessor systems, it results in better performance since a busy lock retry is cheaper than a process context switch.</P>
<P>The <CODE>vmo</CODE> line sets up the size and number of large pages. If you look at the Java command-line switches, you will see that<CODE> -Xlp</CODE> is being used. This enables Large page support in Java, which is described in more detail <A href="http://www.ibm.com/servers/aix/whitepapers/large_page.html" target=new><FONT color=#5c81a7>here</FONT></A>. If you have a memory intensive application, you can experiment with large pages to see if it helps. More information on this topic is available in the SDK Guide accompanying Java.</P>
<P>The<CODE> setsched</CODE> line is actually a script, not an AIX command. It calls the<CODE> thread_setsched</CODE> kernel service to select fixed-priority round-robin scheduling, with a fixed priority of 40 for the Java process.</P>
<P>The <CODE>schedtune</CODE> command is also a script from AIX 5.2 onwards, that maps the passed parameters to the new <CODE>schedo</CODE> command. The above line is changing the time slice for fixed-priority threads to 400 ticks. It is also forcing the fixed priority threads to reside in the global run queue.</P>
<P>Finally, the <CODE>vmtune</CODE> command translates the call to an equivalent vmo call, and the above line enables pinning of shared memory segments. </P>
<P>So you can see that the system was switched to large pages and a fixed priority scheduler, to get record numbers with SpecJBB 2000 benchmark. Can you use these same settings in your application? Probably not, but you are now in a position to examine these commands and the scheduling policies, and experiment to suit your application characteristics. This is the next step in Performance tuning.</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/eserver/library/es-Javaperf5.html#main"><B><FONT color=#996699 size=2>Back to top</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=N101AA><SPAN class=atitle><FONT face=Arial size=4>Case Studies</FONT></SPAN></A></P>
<P>In this section we look at a few examples, taken from actual issues handled by Java service team. These examples should give you a good idea of how to approach performance tuning, and how to use various tools to gather information that can be used for tuning exercise. Note that the cases for this section were not chosen based on how frequently the problem is encountered in the field. The emphasis is on understanding how to use the various tools and techniques discussed in the series to locate and correct performance issues. </P>
<P><A name=N101B3><SPAN class=smalltitle><STRONG><FONT face=Arial>Case 1: Bad application response time</FONT></STRONG></SPAN></A></P>
<P>The reported issue was that the Java-based application's response time was unacceptable. Using<CODE> topas</CODE>, and then with <CODE>vmstat</CODE>, it was seen that Java was the application consuming most CPU. Using tprof, the functions that showed up had GC-related terms in them (e.g. localMark, which is used in Mark phase), so this indicated a possible issue with Java heap sizing. </P>
<P>Looking at GC logs confirmed that the heap was expanding very often. This, combined with multiple allocation failures in quick succession and a very full heap, resulted in the Java application spending a lot of time just trying to locate a free chunk, not finding it, expanding the heap, and then satisfying only the current allocation request.</P>
<P>This was fixed by specifying a larger value for <CODE>-Xmine</CODE>, forcing the heap to grow faster (see<A href="http://www.ibm.com/developerworks/eserver/library/es-Javaperf3.html#mem003" target=new><FONT color=#5c81a7>Tip MEM003</FONT></A> in Part 3). The result was that a single expansion avoided multiple potential allocation failures.</P>
<P>The first step, using AIX tools, confirmed this to be a Java-related issue. The second step, guided by the fact that AIX tools indicated this to be a problem related with GC, could concentrate on GC logs directly. The third step used the available tuning parameters to break the unusual cycle that the application was getting into, allowing the excessive CPU time to be recovered.</P>
<P><A name=N101D6><SPAN class=smalltitle><STRONG><FONT face=Arial>Case 2: JVMPI to the rescue </FONT></STRONG></SPAN></A></P>
<P>Another interesting scenario was raised as a performance issue, with CPU being busy most of the time. Looking at verbosegc, we could see that a GC cycle was being called a bit too frequently, resulting in the application spending most of its time doing just GC.</P>
<P>The verbosegc traces showed that most of the GC activity was being caused due to multiple allocations of very large objects, roughly 10 MB or more in size. These were fragmenting the heap, making the GC cycles longer and thus affecting the performance. But looking at the verbosegc cycle, the customer could not say what these objects were.</P>
<P>The easiest way to locate the culprit would have been to analyze the heapdump using HeapRoots tool. But there was another twist to the situation: the large objects were not surviving the GC cycle. So the heapdump did not show any objects of such size. </P>
<P>This is a classic example of how profiling can be a very useful ally for locating and correcting problems in application sources. The Java Virtual Machine Profile Interface makes this problem trivial. For this particular example, we used a variation of the method described at <A href="http://access1.sun.com/techarticles/JVMPI.html" target=new><FONT color=#5c81a7>Using JVMPI to Identify Large Memory Allocations</FONT></A>, and were able to quickly identify the code that was doing this allocation. </P>
<P><A name=N101ED><SPAN class=smalltitle><STRONG><FONT face=Arial>Case 3: Unbounded growth</FONT></STRONG></SPAN></A></P>
<P>As the final case study for this article, we discuss a scenario that showed up looking like a simple sizing problem. An attempt was being made to scale the application to a 1000 users, and the application would run out of Java heap. Calculating the heap requirements based on the number of users, the heap size was increased from 1 GB to 1.5 GB.</P>
<P>But this triggered OOM errors not coming because of Java heap. The Java heap would show enough free space, but the application logs would show that an OOM occurred. Using <CODE>svmon</CODE>, it was seen that only around 4 segments, or 1 GB, were being used for native heap, and the fourth segment seemed to be almost empty (see "<A href="http://www.ibm.com/developerworks/eserver/articles/aix4java1.html#balmem" target=new><FONT color=#5c81a7>Balancing Memory</FONT></A>" in "<A href="http://www.ibm.com/developerworks/eserver/articles/aix4java1.html" target=new><FONT color=#5c81a7>Getting more memory in AIX for your Java applications</FONT></A>"). </P>
<P>To dig further, the command-line switch <CODE>-verbose:jni</CODE> was added. The extra messages being printed as a result of this switch revealed that the global JNI reference pool was getting exhausted, which is a very rare thing to happen. The global JNI reference pool is large enough to ensure that most normal applications never run even close to exhausting it. </P>
<P>For some time we tried to work around the problem by increasing the number of JNI references (if you specify a higher -Xoss value, it increases the limit in a proportional manner). But this only delayed the inevitable, and the severe Java heap fragmentation caused by the large number of pinned JNI references did not help either.</P>
<P>A closer look at the application design revealed the true cause: an unbounded number of threads being created by the application. As the tests proceeded, the threads would wait for finalizers, and since finalizers are not predictable, there would be a large number of these threads waiting to release their JNI references. The only feasible solution in this case was to change the application code to correct these two problems. Once the application replaced the unbounded threads with a thread pool, and replaced finalizers wherever possible, the sizing work completed with flying colors.</P>
<P>This case shows how you can sometimes end up trying to hit a moving target. An issue reported as Java heap exhaustion eventually turned out to be a design problem. Balancing the Java and Native heaps is usually a critical part of performance tuning, but in this case it was not sufficient. Having so many tools and techniques at your disposal gives you a much broader picture, allowing you to make informed decisions about what to tune. </P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/eserver/library/es-Javaperf5.html#main"><B><FONT color=#996699 size=2>Back to top</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=N10217><SPAN class=atitle><FONT face=Arial size=4>Conclusion</FONT></SPAN></A></P>
<P>This article concludes the series. We hope you find this series a valuable guide in maximizing the performance of your Java applications on AIX.</P>
<P>The authors thank Ashok Ambati, Rajesh Jeyapaul, Sharad Ballal, Roger Leuckie and Mark Bluemel for their input and advice on these articles. A special note of thanks goes to John Tesch, whose collection of information on AIX Java performance was a major source of inspiration to the series. </P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/eserver/library/es-Javaperf5.html#main"><B><FONT color=#996699 size=2>Back to top</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=resources><SPAN class=atitle><FONT face=Arial size=4>Resources</FONT></SPAN></A></P><B>Java on AIX</B> 
<UL>
<LI>The <A href="http://www.ibm.com/developerworks/java/jdk/aix/service.html"><FONT color=#5c81a7>IBM developer kits for AIX, Java technology edition</FONT></A>, at http://www-106.ibm.com/developerworks/java/jdk/aix/service.html, contains links to all available Java releases on AIX. The SDK Guide for each version contains information relevant to the release and should be reviewed before you use that particular version. Another useful link is "Fix Info" link that, among other things, gives you a list of APARs you will need to apply to update all the filesets to the latest level. <BR><BR>
<LI>The <A href="http://www.ibm.com/developerworks/java/jdk/diagnosis/"><FONT color=#5c81a7>IBM developer kits - diagnosis documentation</FONT></A>, at http://www-106.ibm.com/developerworks/java/jdk/diagnosis/, is a comprehensive document that provides thorough coverage of diagnostic capabilities of IBM Java. Best of all, it is platform-independent, so you can use it for all platforms that IBM Java runs on. A recent addition on this page is the "IBM Garbage Collection and Storage Allocation Techniques", a detailed look at how IBM Java implements Garbage Collection and is a must-read for anyone interested in finding out more about how IBM Java works.<BR><BR>
<LI><A href="http://www.ibm.com/developerworks/eserver/library/es-JavaOnAix_install.html"><FONT color=#5c81a7>Implementing Java on AIX</FONT></A> (<I>developerworks</I>, March 2004), at http://www-106.ibm.com/developerworks/eserver/library/es-JavaOnAix_install.html, contains information you need to start using Java on AIX.<BR><BR>
<LI>Since GC tuning is usually the most important part of performance tweaking, see <A href="http://www.ibm.com/developerworks/library/i-gctroub/"><FONT color=#5c81a7>Fine-tuning Java garbage collection performance</FONT></A> (<I>developerworks</I>, January 2003), at http://www-106.ibm.com/developerworks/library/i-gctroub/, for a discussion of tweaking IBM Java heap characteristics. Note that this article is not AIX-specific. Parts 2 and 3 of the current series also look at some tips useful for GC tuning. <BR><BR>
<LI><A href="http://www.ibm.com/developerworks/eserver/articles/aix4java1.html"><FONT color=#5c81a7>Getting more memory in AIX for your Java applications</FONT></A> (<I>developerworks</I>, September, 2003), at http://www-106.ibm.com/developerworks/eserver/articles/aix4java1.html, provides information for heaps larger than 1 GB. <BR></LI></UL><B>AIX</B> 
<UL>
<LI><A href="http://publib-b.boulder.ibm.com/Redbooks.nsf/RedbookAbstracts/SG246039.html"><FONT color=#5c81a7>AIX 5L Performance Tools Handbook</FONT></A> at http://publib-b.boulder.ibm.com/Redbooks.nsf/RedbookAbstracts/SG246039.html, covers each of the performance tools described in the current series (plus many more that are not). You will find practical advice on how to use a particular tool, examples of tool usage, and hints on how to interpret and use the information obtained using these tools.<BR><BR>
<LI><A href="http://publib-b.boulder.ibm.com/Redbooks.nsf/RedbookAbstracts/SG244810.html"><FONT color=#5c81a7>Understanding IBM eServer pSeries Performance and Sizing</FONT></A> at http://publib-b.boulder.ibm.com/Redbooks.nsf/RedbookAbstracts/SG244810.html, gives an overview of how IBM AIX-based systems are designed. If you are planning to do a benchmark exercise, this book is invaluable.<BR><BR>
<LI>A series of three articles that talk about the changes to Performance tools in AIX 5.2 are <A href="http://www.ibm.com/developerworks/eserver/articles/Keung_AIXPerf.html"><FONT color=#5c81a7>Part 1</FONT></A> (developerworks, July 2003), <A href="http://www.ibm.com/developerworks/eserver/articles/AIX5.2PerfTools.html"><FONT color=#5c81a7>Part 2</FONT></A> (developerworks, November 2003), and <A href="http://www.ibm.com/developerworks/eserver/articles/AIX5.2_performancetoolsupdatepart3.html"><FONT color=#5c81a7>Part 3 (developerworks, January 2004</FONT></A>. AIX 5.2 did a major overhaul of some of the familiar performance tools and this series covers the changes very well.<BR><BR>
<LI>The <A href="ftp://ftp.software.ibm.com/aix/tools/perftools/perfpmr/"><FONT color=#5c81a7>AIX Performance PMR Data Collection Tools</FONT></A>, described in Part 1, can be downloaded from ftp://ftp.software.ibm.com/aix/tools/perftools/perfpmr/<BR><BR>
<LI><A href="http://www.ibm.com/developerworks/java/library/j-perf-checklist/"><FONT color=#5c81a7>Checklist for isolating Java performance problems on AIX servers</FONT></A> at http://www-106.ibm.com/developerworks/java/library/j-perf-checklist/<BR></LI></UL><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/eserver/library/es-Javaperf5.html#main"><B><FONT color=#996699 size=2>Back to top</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=author><SPAN class=atitle><FONT face=Arial size=4>About the authors</FONT></SPAN></A></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD colSpan=2><FONT face=Arial size=4><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width="100%"></FONT></TD></TR>
<TR vAlign=top align=left>
<TD>
<P><FONT face=Arial size=4></FONT></P></TD>
<TD>
<P>Amit Mathur works in the IBM Solutions Development group, working primarily with IBM ISVs in enablement/performance of their apps on IBM eServer platforms and providing self-sufficiency to ISVs and customers by providing education and articles on developer works. Amit has more than fourteen years' experience working in Leading software support and development in C/C++, Java and databases on UNIX and Linux platforms. He holds a Bachelor of Engineering degree in Electronics and Telecommunication from India. You can reach Amit at amitmat@us.ibm.com.</P></TD></TR></TBODY></TABLE><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD colSpan=2><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width="100%"></TD></TR>
<TR vAlign=top align=left>
<TD>
<P></P></TD>
<TD>
<P>Sumit Chawla leads the Java Enablement initiative for IBM eServer (for AIX, Windows, and Linux platforms), assisting Independent Software Vendors for IBM Servers. Sumit has a Master of Science degree in Computer Science, with almost 10 years of experience in the IT industry, and is certified by IBM as an Application Architect. He is a frequent contributor to the developerWorks eServer zone. You can contact him at sumitc@us.ibm.com.</P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/jacky/aggbug/25875.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jacky/" target="_blank">jacky</a> 2005-12-29 14:04 <a href="http://www.blogjava.net/jacky/articles/25875.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java 理论与实践: JVM 1.4.1 中的垃圾收集[转]</title><link>http://www.blogjava.net/jacky/articles/25874.html</link><dc:creator>jacky</dc:creator><author>jacky</author><pubDate>Thu, 29 Dec 2005 05:59:00 GMT</pubDate><guid>http://www.blogjava.net/jacky/articles/25874.html</guid><wfw:comment>http://www.blogjava.net/jacky/comments/25874.html</wfw:comment><comments>http://www.blogjava.net/jacky/articles/25874.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jacky/comments/commentRss/25874.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jacky/services/trackbacks/25874.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width="100%">
<H1><SPAN style="COLOR: #999999">Java 理论与实践: </SPAN>JVM 1.4.1 中的垃圾收集</H1>
<P id=subtitle>分代垃圾收集和并发垃圾收集</P><IMG class=display-img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=1></TD>
<TD class=no-print width=192><IMG height=18 alt=developerWorks src="http://www-128.ibm.com/developerworks/i/dw.gif" width=192></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD width="100%">
<TABLE class=no-print cellSpacing=0 cellPadding=0 width=160 align=right border=0>
<TBODY>
<TR>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD>
<TABLE cellSpacing=0 cellPadding=0 width=150 border=0>
<TBODY>
<TR>
<TD class=v14-header-1-small>文档选项</TD></TR></TBODY></TABLE>
<TABLE class=v14-gray-table-border cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=no-padding width=150>
<TABLE cellSpacing=0 cellPadding=0 width=143 border=0><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8> 
<FORM name=email action=https://www-130.ibm.com/developerworks/secure/email-it.jsp><INPUT type=hidden value="在上月的 Java 理论与实践中，专栏作家 Brian Goetz 回顾了垃圾收集的基本算法。本月，他进一步探讨 JVM 1.4.1 是如何实际处理垃圾收集的，包括一些针对多处理器系统的新垃圾收集选项。请在本文对应的讨论论坛上与作者及其他读者分享您对本文的心得（您也可以单击文章顶部或底部的讨论来访问该论坛）。" name=body><INPUT type=hidden value="Java 理论与实践: JVM 1.4.1 中的垃圾收集" name=subject><INPUT type=hidden value=cn name=lang>
<SCRIPT language=JavaScript type=text/javascript>
<!--
document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt=""/></td><td width="16"><img src="//www.ibm.com/i/v14/icons/em.gif" height="16" width="16" vspace="3" alt="将此页作为电子邮件发送" /></td><td width="122"><p><a class="smallplainlink" href="javascript:document.email.submit();"><b>将此页作为电子邮件发送</b></a></p></td></tr>');
//-->
</SCRIPT>
 
<TBODY>
<TR vAlign=top>
<TD width=8><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></TD>
<TD width=16><IMG height=16 alt=将此页作为电子邮件发送 src="http://www.ibm.com/i/v14/icons/em.gif" width=16 vspace=3></TD>
<TD width=122>
<P><A class=smallplainlink href="javascript:document.email.submit();"><B><FONT face=Verdana color=#5c81a7 size=2>将此页作为电子邮件发送</FONT></B></A></P></TD></TR><NOSCRIPT><tr valign="top"><td width="8"><img alt="" height="1" width="8" src="//www.ibm.com/i/c.gif"/></td><td width="16"><img alt="" width="16" height="16" src="//www.ibm.com/i/c.gif"/></td><td class="small" width="122"><p><span class="ast">未显示需要 JavaScript 的文档选项</span></p></td></tr></NOSCRIPT></FORM></TBODY></TABLE></TD></TR></TBODY></TABLE><BR>
<TABLE cellSpacing=0 cellPadding=0 width=150 border=0>
<TBODY>
<TR>
<TD class=v14-header-1-small>对此页的评价</TD></TR></TBODY></TABLE>
<TABLE class=v14-gray-table-border cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=no-padding width=150>
<TABLE cellSpacing=0 cellPadding=0 width=143 border=0>
<TBODY>
<TR vAlign=top>
<TD width=8><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></TD>
<TD><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/d_bold.gif" width=16 vspace=3 border=0></TD>
<TD width=125>
<P><A class=smallplainlink href="http://www-128.ibm.com/developerworks/cn/java/j-jtp11253/#rate"><B><FONT face=Verdana color=#996699 size=2>帮助我们改进这些内容</FONT></B></A></P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR></TD></TR></TBODY></TABLE>
<P>级别: 初级</P>
<P><A href="http://www-128.ibm.com/developerworks/cn/java/j-jtp11253/#author"><FONT color=#996699>Brian Goetz</FONT></A>, 首席顾问, Quiotix Corp<BR></P>
<P>2003 年 12 月 01 日</P>
<BLOCKQUOTE>在上月的 <I>Java 理论与实践</I>中，专栏作家 Brian Goetz 回顾了垃圾收集的基本算法。本月，他进一步探讨 JVM 1.4.1 是如何实际处理垃圾收集的，包括一些针对多处理器系统的新垃圾收集选项。请在本文对应的 <A href="javascript:void forumWindow()"><FONT color=#5c81a7>讨论论坛</FONT></A>上与作者及其他读者分享您对本文的心得（您也可以单击文章顶部或底部的 <B>讨论</B>来访问该论坛）。 </BLOCKQUOTE>
<P><A href="http://www-128.ibm.com/developerworks/cn/java/j-jtp10283/"><FONT color=#5c81a7>上个月</FONT></A>，我们分析了引用计数、复制、标记-清除和标记-整理这些经典的垃圾收集技术。其中每一种方法在特定条件下都有其优点和缺点。例如，当有很多对象成为垃圾时，复制可以做得很好，但是有许多长寿对象时它就变得很糟（要反复复制它们）。相反，标记-整理对于长寿对象可以做得很好（只复制一次），但是当有许多短寿对象时就没有那么好了。JVM 1.2 及以后版本使用的技术称为 <I>分代垃圾收集（generational garbage collection）</I>,它结合了这两种技术以结合二者的长处，结果就是对象分配开销非常小。 </P>
<P><A name=1><SPAN class=atitle><STRONG><FONT size=4>老对象和年轻对象</FONT></STRONG></SPAN></A></P>
<P>在任何一个应用程序堆中，一些对象在创建后很快就成为垃圾，另一些则在程序的整个运行期间一直保持生存。经验分析表明，对于大多数面向对象的语言，包括 Java 语言，绝大多数对象――可以多达 98%（这取决于您对年轻对象的衡量标准）是在年轻的时候死亡的。可以用时钟秒数、对象分配以后�h内存管理子系统分配的总字节或者对象分配后经历的垃圾收集的次数来计算对象的寿命。但是不管您如何计量，分析表明了同一件事――大多数对象是在年轻的时候死亡的。大多数对象在年轻时死亡这一事实对于收集器的选择很有意义。特别是，当大多数对象在年轻时死亡时，复制收集器可以执行得相当好，因为复制收集器完全不访问死亡的对象，它们只是将活的对象复制到另一个堆区域中，然后一次性收回所有的剩余空间。</P>
<P>那些经历过第一次垃圾收集后仍能生存的对象，很大部分会成为长寿的或者永久的对象。根据短寿对象和长寿对象的混合比例，不同垃圾收集策略的性能会有非常大的差别。当大多数对象在年轻时死亡时，复制收集器可以工作得很好，因为年轻时死亡的对象永远不需要复制。不过，复制收集器处理长寿对象却很糟糕，它要从一个半空间向另一个半空间反复来回复制这些对象。相反，标记-整理收集器对于长寿对象可以工作得很好，因为长寿对象趋向于沉在堆的底部，从而不用再复制。不过，标记-清除和标记-理整收集器要做很多额外的分析死亡对象的工作，因为在清除阶段它们必须分析堆中的每一个对象。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-jtp11253/#main"><B><FONT face=Verdana color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=2><SPAN class=atitle><STRONG><FONT size=4>分代收集</FONT></STRONG></SPAN></A></P>
<P>分代收集器(generializational collector)将堆分为多个代。在年轻的代中创建对象，满足某些提升标准的对象，如经历了特定次数垃圾收集的对象，将被提升到下一更老的代。分代收集器对不同的代可以自由使用不同的收集策略，对各代分别进行垃圾收集。</P>
<P><A name=N10076><SPAN class=smalltitle><STRONG>小的收集</STRONG></SPAN></A></P>
<P>分代收集的一个优点是它不同时收集所有的代，因此可以使垃圾收集暂停更短。当分配器不能满足分配请求时，它首先触发一个 <I>小的收集（minor collection）</I>，它只收集最年轻的代。因为年轻代中的许多对象已经死亡，复制收集器完全不用分析死亡的对象，所以小的收集的暂停可以相当短并通常可以回收大量的堆空间。如果小的收集释放了足够的堆空间，那么用户程序就可以立即恢复。如果它不能释放足够的堆空间，那么它就继续收集上一代，直到回收了足够的内存。（在垃圾收集器进行了全部收集以后仍不能回收足够的内存时，它将扩展堆或者抛出 <CODE><FONT face=Courier size=2>OutOfMemoryError</FONT></CODE> ）。 </P>
<P><A name=N10086><SPAN class=smalltitle><STRONG>代间引用</STRONG></SPAN></A></P>
<P>跟踪垃圾收集器，如复制、标记-清除和标记-整理等垃圾收集器,都是从根集（root set）开始扫描，遍历对象间的引用，直到访问了所有活的对象。</P>
<P>分代跟踪收集器从根集开始，但是并不遍历指向更老一代中对象的引用，这减少了要跟踪的对象图的大小。但是这也带来一个问题――如果更老一代中的对象引用一个不能通过从根开始的所有其他引用链到达的更年轻的对象该怎么办？</P>
<P>为了解决这个问题，分代收集器必须显式地跟踪从老对象到年轻对象的引用并将这些老到年轻的引用加入到小的收集的根集中。有两种创建从老对象到年轻对象的引用的方法。要么是将老对象中包含的引用修改为指向年轻对象，要么是将引用其他年轻对象的年轻对象提升为更老的一代。</P>
<P><A name=N10095><SPAN class=smalltitle><STRONG>跟踪代间引用</STRONG></SPAN></A></P>
<P>不管一个老到年轻的引用是通过提升还是指针修改创建的，垃圾收集器在进行小的收集时需要有全部老到年轻的引用。做到这一点的一种方法是跟踪老的代，但是这显然有很大的开销。更好的一种方法是线性扫描老的代以查找对年轻对象的引用。这种方法比跟踪更快并有更好的区域性（locality），但是仍然有很大的工作量。</P>
<P>赋值函数（mutator）和垃圾收集器可以共同工作以在创建老到年轻的引用时维护它们的完整列表。当对象提升为更老一代时，垃圾收集器可以记录所有由于这种提升而创建的老到年轻的引用，这样就只需要跟踪由指针修改所创建的代间引用。</P>
<P>垃圾收集器可以有几种方法跟踪由于修改现有对象中的引用而产生的老到年轻的引用。它可以使用在引用计数收集器中维护引用计数的同样方法（编译器可以生成围绕指针赋值的附加指令）跟踪它们，也可以在老一代堆上使用虚拟内存保护以捕获向老对象的写入。另一种可能更有效的虚拟内存方法是在老一代堆中使用页修改脏位（page modification dirty bit），以确定为找到包含老到年轻指针的对象时要扫描的块。</P>
<P>用一点小技巧，就可以避免跟踪每一个指针修改并检查它是否跨越代边界的开销。例如，不需要跟踪针对本地或者静态变量的存储，因为它们已经是根集的一部分了。也可以避免跟踪存储在某些构造函数中的指针，这些构造函数只用于初始化新建对象的字段（即所谓 <I>初始化存储（initializing stores）</I>），因为（几乎）所有对象都是分配到年轻代中。不管是什么情况，运行库都必须维护一个老对象到年轻对象的引用集并在收集年轻代时将这些引用添加到根集中。 </P>
<P>在图 1 中，箭头表示堆中对象间的引用。红色箭头表示必须添加到根集中供小的收集使用的老到年轻的引用。蓝色箭头表示从根集或者年轻代到老对象的引用，在只收集年轻代时不需要跟踪它们。</P><BR><A name=fig1><B>图 1. 代间引用 </B></A><BR><IMG height=236 alt=代间引用 src="http://www-128.ibm.com/developerworks/cn/java/j-jtp11253/images/generational.gif" width=440> <BR>
<P><A name=N100BC><SPAN class=smalltitle><STRONG>卡片标记</STRONG></SPAN></A></P>
<P>Sun JDK 使用一种称为 <I>卡片标记（card marking）</I>算法的改进算法以标识对老一代对象的字段中包含的指针的修改。在这种方法中，堆分为一组 <I>卡片</I>，每个卡片一般都小于一个内存页。JVM 维护着一个卡片映射，对应于堆中的每一个卡片都有一个位（在某些实现中是一个字节）。每次修改堆中对象中的指针字段时，就在卡片映射中设置对应那张卡片的相应位。在垃圾收集时，就对与老一代中卡片相关联的标记位进行检查，对脏的卡片扫描以寻找对年轻代有引用的对象。然后清除标记位。卡片标记有几项开销――卡片映射所需的额外空间、对每一个指针存储所做的额外工作，以及在垃圾收集时做的额外工作。对每一个非初始化堆指针存储，卡片标记算法可以只增加两到三个机器指令，并要求在小的收集时对所有脏卡片上的对象进行扫描。 </P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-jtp11253/#main"><B><FONT face=Verdana color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=3><SPAN class=atitle><STRONG><FONT size=4>JDK 1.4.1 默认收集器</FONT></STRONG></SPAN></A></P>
<P>在默认情况下，JDK 1.4.1 将堆分为两部分，一个年轻的代和一个老的代（实际上，还有第三部分――永久空间，它用于存储装载的类和方法对象）。借助于复制收集器，年轻的代又分为一个创建空间（通常称为 <I>Eden</I>）和两个生存半空间。 </P>
<P>老的代使用标记-整理收集器。对象在经历了几次复制后提升到老的代。小的收集将活的对象从 Eden 和一个生存半空间复制到另一个生存半空间，并可能提升一些对象到老的代。大的收集（major collection）既会收集年轻的代，也会收集老的代。 <CODE><FONT face=Courier size=2>System.gc()</FONT></CODE> 方法总是触发一个大的收集，这就是应该尽量少用（如果不能完全不用的话） <CODE><FONT face=Courier size=2>System.gc() </FONT></CODE>的原因之一，因为大的收集要比小的收集花费长得多的时间。没有办法以编程方式触发小的收集。 </P>
<P><A name=N100E2><SPAN class=smalltitle><STRONG>其他收集选项</STRONG></SPAN></A></P>
<P>除了默认情况下使用的复制收集器和标记-整理收集器，JDK 1.4.1 还包含其他四种垃圾收集算法，每一种适用于不同的目的。JDK 1.4.1 包含一个增量收集器（自 JDK 1.2 就已经出现了）和三种在多处理器系统中进行更有效收集的新收集器――并行复制收集器、并行清除（scavenging）收集器和并发标记-清除收集器。这些新收集器是为了解决在多处理器系统中垃圾收集器成为伸缩性瓶颈这一问题的。图 2 显示了在什么时候选择备用收集选项的指导。</P><BR><A name=fig2><B>图 2. 1.4.1 垃圾收集选项（Folgmann IT-Consulting 提供） </B></A><BR><IMG height=253 alt=垃圾收集选项 src="http://www-128.ibm.com/developerworks/cn/java/j-jtp11253/images/gc-options.gif" width=593> <BR>
<P><A name=N100FA><SPAN class=smalltitle><STRONG>增量收集</STRONG></SPAN></A></P>
<P>增量收集选项自 1.2 起就成为 JDK 的一部分。增量收集减少了垃圾收集暂停，以牺牲吞吐能力为代价，这使它只在更短的收集暂停非常重要时才值得考虑，如接近实时的系统。</P>
<P><I>Train</I>算法是 JDK 用于增量收集的算法，它在堆中老的代和年轻的代之间创建一个新区域。这些堆区域划分为“火车（train）”，每个火车又分为一系列的“车厢（car）”。每个车厢可以分别收集。结果，每个火车车厢组成单独的一代，这意味着不但要跟踪老到年轻的引用，而且还要跟踪从老的火车到年轻的火车以及老的车厢到年轻的车厢的引用。这为赋值函数（mutator）和垃圾收集器带来了大量的额外工作，但是可以得到更短的收集暂停。 </P>
<P><A name=N10109><SPAN class=smalltitle><STRONG>并行收集器和并发收集器</STRONG></SPAN></A></P>
<P>JDK 1.4.1 中新的收集器都是为解决多处理器系统中垃圾收集器的问题而设计的。因为大多数垃圾收集算法会在一段时间里使系统停止，单线程的收集器很快会成为伸缩性瓶颈，因为在垃圾收集器将用户程序线程挂起时，除了一个处理器之外，其他的处理器都是空闲的。新收集器中的两个――并行复制收集器和并发标记-清除收集器――设计为减少收集暂停时间。另一个是并行清除收集器，它是为在大堆上的更高吞吐能力而设计的。</P>
<P>并行复制收集器用 JVM 选项 <CODE><FONT face=Courier size=2>-XX:+UseParNewGC</FONT></CODE> 启用，是一个年轻代复制收集器，它将垃圾收集的工作分为与 CPU 数量一样多的线程。并发标记-清除收集器由 <CODE><FONT face=Courier size=2>-XX:+UseConcMarkSweepGC</FONT></CODE> 选项启用，它是一个老代标记-清除收集器，它在初始标记阶段（及在以后暂短重新标记阶段）暂短地停止整个系统，然后恢复用户程序，同时垃圾收集器线程与用户程序并发地执行。并行复制收集器和并发标记-清除收集器基本上是默认的复制收集器和标记-整理收集器的并发版本。由 <CODE><FONT face=Courier size=2>-XX:+UseParallelGC</FONT></CODE> 启用的并行清除收集器是年轻代收集器，针对多处理器系统上非常大(吉字节以及更大的)堆进行了优化。 </P>
<P><A name=N10121><SPAN class=smalltitle><STRONG>选择一种算法</STRONG></SPAN></A></P>
<P>有六种算法可以选择，您可能不知道要使用哪一种。 <A href="http://www-128.ibm.com/developerworks/cn/java/j-jtp11253/#fig2"><FONT color=#996699>图 2</FONT></A>提供了一些指导，将收集器分为单线程和并发的，以及分为短暂停和高吞吐能力的。只要您掌握了应用程序和部署环境的信息，就足以选择合适的算法。对于许多应用程序，默认的收集器可以工作得很好――因此如果您没有性能问题，那么就没必要加入更多的复杂性。不过，如果您的应用程序是部署在多处理器系统上或者使用非常大的堆，那么改变收集器选项可能会有巨大的性能提升。 </P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-jtp11253/#main"><B><FONT face=Verdana color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=4><SPAN class=atitle><STRONG><FONT size=4>微调垃圾收集器</FONT></STRONG></SPAN></A></P>
<P>JDK 1.4.1 还包括大量的微调垃圾收集的选项。调整这些选项并衡量它们的效果可能会花费您大量时间，因此在试图微调垃圾收集器之前先对您的应用程序进行彻底的配置（profile）和优化，这样您的微调工作可能会得到更好的结果。</P>
<P>微调垃圾收集首先要做的是检查冗长的 GC 输出。这会使您得到垃圾收集操作的频率、定时和持续时间等信息。最简单的垃圾收集微调就是扩大最大堆的大小（ <CODE><FONT face=Courier size=2>-Xmx</FONT></CODE> ）。随着堆的增大，复制收集会变得更有效，所以在增大堆时，您就减少了每个对象的收集成本。除了增加最大堆的大小，还可以用选项 <CODE><FONT face=Courier size=2>-XX:NewRatio</FONT></CODE> 增加分配给年轻代的空间份额。也可以用 <CODE><FONT face=Courier size=2>-Xmn</FONT></CODE> 选项显式指定年轻代的大小。有关微调垃圾收集的更多细节请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-jtp11253/#resources"><FONT color=#996699>参考资料</FONT></A>中的几篇文章。 </P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-jtp11253/#main"><B><FONT face=Verdana color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=5><SPAN class=atitle><STRONG><FONT size=4>结束语</FONT></STRONG></SPAN></A></P>
<P>随着 JVM 的发展，默认垃圾收集器变得越来越好了。JDK 1.2 及以后版本所使用的分代垃圾收集器提供了比早期 JDK 所使用的标记-清除-整理收集器好得多的分配和收集性能。JDK 1.4.1 通过增加新的针对多处理器系统和非常大的堆的多线程收集选项，进一步改进了垃圾收集的效率。</P>
<P>下个月，我们将讨论一些有关垃圾收集的性能神话（hints and myths），包括对象分配的真实成本、显式赋空的代价和好处以及结束（finalization）的代价，以此来完成我们对垃圾收集的探讨。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-jtp11253/#main"><B><FONT face=Verdana color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=resources><SPAN class=atitle><STRONG><FONT size=4>参考资料 </FONT></STRONG></SPAN></A></P>
<UL>
<LI>您可以参阅本文在 developerWorks 全球站点上的 <A href="http://www.ibm.com/developerworks/library/j-jtp11253/index.html"><FONT color=#5c81a7>英文原文</FONT></A>. <BR><BR>
<LI>请在 <A href="javascript:void forumWindow()"><FONT color=#5c81a7>讨论论坛</FONT></A>上参与本文的讨论。（您也可以通过单击文章顶部或底部的 <B>讨论</B>来访问该论坛。） <BR><BR><BR>
<LI>阅读 Brian Goetz 的全部 <A href="http://www-128.ibm.com/developerworks/cn/java/j-jtp/"><FONT color=#5c81a7><I>Java 理论与实践</I> </FONT></A>系列。 <BR><BR><BR>
<LI><I><A href="http://www.amazon.com/exec/obidos/ASIN/0471941484/ref=nosim/none0b69"><FONT color=#5c81a7>Garbage Collection: Algorithms for Automatic Dynamic Memory Management</FONT></A> </I>（John Wiley &amp; Sons， 1997 年）是对垃圾收集算法的全面评述，包括大量参考书目。作者 Richard Jones 在其 <A href="http://www.cs.kent.ac.uk/people/staff/rej/gc.html"><FONT color=#5c81a7>垃圾收集页</FONT></A>上维护着一个接近 2000 页的不断更新的参考书目。 <BR><BR><BR>
<LI>垃圾收集邮件列表上有一个 <A href="http://www.iecc.com/gclist/GC-faq.html"><FONT color=#5c81a7>垃圾收集常见问题</FONT></A>。 <BR><BR><BR>
<LI>针对 Java 平台的 IBM 1.4 Developer Kits 使用了标记-清除-整理收集器，它支持 <A href="http://www.ibm.com/developerworks/ibm/library/i-incrcomp/"><FONT color=#5c81a7>增量整理</FONT></A>以减少暂停时间。 <BR><BR><BR>
<LI>包括三篇文章的“ <A href="http://www.ibm.com/developerworks/ibm/library/i-garbage1/"><FONT color=#5c81a7>Sensible sanitation -- Understanding the IBM Java Garbage Collector</FONT></A>”系列（ <I>developerWorks</I>，2002 年 8 月 - 9 月）描述了针对 Java 平台的 IBM 1.2 和 1.3 Developer Kits 使用的垃圾收集策略。 <BR><BR><BR>
<LI>“ <A href="http://www.ibm.com/developerworks/ibm/library/i-gctroub/"><FONT color=#5c81a7>Fine-tuning Java garbage collection performance</FONT></A>”（ <I>developerWorks</I>，2003 年 1 月）描述了如何发现和排除垃圾收集问题。 <BR><BR><BR>
<LI><I>IBM Systems Journal</I>的这篇文章描述了构建针对 Java 平台的 IBM 1.1.x Developer Kits 时得到的一些 <A href="http://www.research.ibm.com/journal/sj/391/dimpsey.html"><FONT color=#5c81a7>经验</FONT></A>，包括标记-清除和标记-清除-整理垃圾收集的细节。 <BR><BR><BR>
<LI>Sun 的这篇文档提供了 <A href="http://java.sun.com/docs/hotspot/gc/"><FONT color=#5c81a7>对垃圾收集性能微调过程的介绍</FONT></A>。 <BR><BR><BR>
<LI><A href="http://developer.java.sun.com/developer/technicalArticles/ALT/RefObj/"><FONT color=#5c81a7>引用对象</FONT></A>，如 <CODE><FONT face=Courier size=2>WeakReference</FONT></CODE> 和 <CODE><FONT face=Courier size=2>SoftReference</FONT></CODE> ，加入了对确定对象是否可以被垃圾收集的额外考虑。 <BR><BR><BR>
<LI><A href="http://developer.java.sun.com/developer/technicalArticles/Programming/GCPortal/"><FONT color=#5c81a7>GC Portal</FONT></A>是一组跟踪您应用程序的垃圾收集性能的工具。 <BR><BR><BR>
<LI>Sun 的这篇文章描述了 <A href="http://wireless.java.sun.com/midp/articles/garbage/"><FONT color=#5c81a7>Sun JVM 中的垃圾收集以及微调选项</FONT></A>，并说明了如何解释 verbose GC 输出。 <BR><BR><BR>
<LI>在其论文“ <A href="http://www.cs.ucsb.edu/labs/oocsb/papers/write-barrier.pdf"><FONT color=#5c81a7>A fast write barrier for generational garbage collectors</FONT></A>”中，Urs Hoeltze 讨论了经典的卡片-标记算法以及通过稍微增加收集时扫描脏卡片的成本而显著减少标记成本的一个改进。 <BR><BR><BR>
<LI>文中 <A href="http://www-128.ibm.com/developerworks/cn/java/j-jtp11253/#fig2"><FONT color=#996699>图 2</FONT></A>所示的图是由 <A href="http://www.folgmann.de/en/j2ee/gc.html"><FONT color=#5c81a7>Folgmann IT-Consulting</FONT></A>提供的。 <BR><BR><BR>
<LI>这篇文章描述了 <A href="http://www.daimi.au.dk/~beta/Papers/Train/train.html"><FONT color=#5c81a7>火车增量垃圾收集算法</FONT></A>的细节。 <BR><BR><BR>
<LI>在 <A href="http://www-128.ibm.com/developerworks/cn/java/"><FONT color=#996699><I>developerWorks</I>Java 技术专区 </FONT></A>可找到数百篇有关 Java 技术的参考资料。 <BR><BR></LI></UL><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-jtp11253/#main"><B><FONT face=Verdana color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=author><SPAN class=atitle><STRONG><FONT size=4>关于作者</FONT></STRONG></SPAN></A></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD colSpan=2><STRONG><FONT size=4><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width="100%"></FONT></STRONG></TD></TR>
<TR vAlign=top align=left>
<TD>
<P><STRONG><FONT size=4></FONT></STRONG></P></TD>
<TD>
<P>Brian Goetz 在过去 15 年间一直从事专业软件开发。他是 <A href="http://www.quiotix.com/"><FONT color=#5c81a7>Quiotix</FONT></A>的首席顾问，该公司是一家位于加利福尼亚州洛斯拉图斯（Los Altos）的软件开发和咨询公司，他也是几个 JCP 专家组的成员。请参阅流行的业界出版物中 Brian 已经 <A href="http://www.quiotix.com/~brian/pubs.html"><FONT color=#5c81a7>发表和即将发表的文章</FONT></A>。您可以通过 <A href="mailto:brian@quiotix.com"><FONT color=#5c81a7>brian@quiotix.com</FONT></A>与 Brian 联系。 </P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/jacky/aggbug/25874.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jacky/" target="_blank">jacky</a> 2005-12-29 13:59 <a href="http://www.blogjava.net/jacky/articles/25874.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>优化 Java 垃圾收集的性能[转]</title><link>http://www.blogjava.net/jacky/articles/25873.html</link><dc:creator>jacky</dc:creator><author>jacky</author><pubDate>Thu, 29 Dec 2005 05:58:00 GMT</pubDate><guid>http://www.blogjava.net/jacky/articles/25873.html</guid><wfw:comment>http://www.blogjava.net/jacky/comments/25873.html</wfw:comment><comments>http://www.blogjava.net/jacky/articles/25873.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jacky/comments/commentRss/25873.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jacky/services/trackbacks/25873.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width="100%">
<H1>优化 Java 垃圾收集的性能</H1>
<P id=subtitle>如何利用 IBM Java 虚拟机检测和解决垃圾收集问题</P><IMG class=display-img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=1></TD>
<TD class=no-print width=192><IMG height=18 alt=developerWorks src="http://www-128.ibm.com/developerworks/i/dw.gif" width=192></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD width="100%">
<TABLE class=no-print cellSpacing=0 cellPadding=0 width=160 align=right border=0>
<TBODY>
<TR>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD>
<TABLE cellSpacing=0 cellPadding=0 width=150 border=0>
<TBODY>
<TR>
<TD class=v14-header-1-small>文档选项</TD></TR></TBODY></TABLE>
<TABLE class=v14-gray-table-border cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=no-padding width=150>
<TABLE cellSpacing=0 cellPadding=0 width=143 border=0><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8> 
<FORM name=email action=https://www-130.ibm.com/developerworks/secure/email-it.jsp><INPUT type=hidden value="您的 Java 应用程序充分利用了所运行的 IBM eServer 硬件的能力了吗？在本文中，作者将介绍如何判断垃圾收集 —— Java 虚拟机执行的收回不再使用空间的后台任务 —— 是否调节到最佳状态。然后，他将提供一些解决垃圾收集问题的建议。" name=body><INPUT type=hidden value="优化 Java 垃圾收集的性能" name=subject><INPUT type=hidden value=cn name=lang>
<SCRIPT language=JavaScript type=text/javascript>
<!--
document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt=""/></td><td width="16"><img src="//www.ibm.com/i/v14/icons/em.gif" height="16" width="16" vspace="3" alt="将此页作为电子邮件发送" /></td><td width="122"><p><a class="smallplainlink" href="javascript:document.email.submit();"><b>将此页作为电子邮件发送</b></a></p></td></tr>');
//-->
</SCRIPT>
 
<TBODY>
<TR vAlign=top>
<TD width=8><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></TD>
<TD width=16><IMG height=16 alt=将此页作为电子邮件发送 src="http://www.ibm.com/i/v14/icons/em.gif" width=16 vspace=3></TD>
<TD width=122>
<P><A class=smallplainlink href="javascript:document.email.submit();"><B><FONT face=Verdana color=#5c81a7 size=2>将此页作为电子邮件发送</FONT></B></A></P></TD></TR><NOSCRIPT><tr valign="top"><td width="8"><img alt="" height="1" width="8" src="//www.ibm.com/i/c.gif"/></td><td width="16"><img alt="" width="16" height="16" src="//www.ibm.com/i/c.gif"/></td><td class="small" width="122"><p><span class="ast">未显示需要 JavaScript 的文档选项</span></p></td></tr></NOSCRIPT></FORM></TBODY></TABLE></TD></TR></TBODY></TABLE><BR>
<TABLE cellSpacing=0 cellPadding=0 width=150 border=0>
<TBODY>
<TR>
<TD class=v14-header-1-small>对此页的评价</TD></TR></TBODY></TABLE>
<TABLE class=v14-gray-table-border cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=no-padding width=150>
<TABLE cellSpacing=0 cellPadding=0 width=143 border=0>
<TBODY>
<TR vAlign=top>
<TD width=8><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></TD>
<TD><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/d_bold.gif" width=16 vspace=3 border=0></TD>
<TD width=125>
<P><A class=smallplainlink href="http://www-128.ibm.com/developerworks/cn/java/i-gctroub/index.html#rate"><B><FONT face=Verdana color=#996699 size=2>帮助我们改进这些内容</FONT></B></A></P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR></TD></TR></TBODY></TABLE>
<P>级别: 初级</P>
<P><A href="http://www-128.ibm.com/developerworks/cn/java/i-gctroub/index.html#author"><FONT color=#996699>Sumit Chawla</FONT></A>, 技术主管，eServer Java Enablement, IBM<BR></P>
<P>2003 年 1 月 01 日</P>
<BLOCKQUOTE>您的 Java 应用程序充分利用了所运行的 IBM eServer 硬件的能力了吗？在本文中，作者将介绍如何判断<I>垃圾收集</I> —— Java 虚拟机执行的收回不再使用空间的后台任务 —— 是否调节到最佳状态。然后，他将提供一些解决垃圾收集问题的建议。</BLOCKQUOTE>
<P><A name=N1004C><SPAN class=atitle><STRONG><FONT size=4>简介</FONT></STRONG></SPAN></A></P>
<P>垃圾收集实现是 IBM Java Virtual Machine（JVM）卓越性能的关键。其他多数 JVM 都需要大量调整才能提供最优性能，而 IBM JVM 利用其“开箱即用”的默认设置，在多数情况下都能工作得很好。但是在某些情况下，垃圾收集的性能会莫名其妙地迅速降低。结果可能导致服务器没有响应、屏幕静止不动或者完全失效，并常常伴有“堆空间严重不足”这类含糊的消息。幸运的是，多数情况下都很容易找到原因，通常也很容易纠正错误。</P>
<P>本文将介绍如何确定造成性能下降的潜在原因。因为垃圾收集是一个很大、很复杂的话题，所以本文是在已经发表的一组相关文章的基础上展开讨论（请参阅<A href="http://www-128.ibm.com/developerworks/cn/java/i-gctroub/index.html#resources"><FONT color=#996699>参考资料</FONT></A>）。虽然本文讨论的多数建议都将 Java 程序看作是一个黑盒子，但仍然有一些观点可以在设计或编码时运用，以避免潜在的问题。</P>
<P>本文提供的信息适用于所有 IBM eServer 平台，除了可重新设置的 JVM 配置（请参阅<A href="http://www-128.ibm.com/developerworks/cn/java/i-gctroub/index.html#resources"><FONT color=#996699>参考资料</FONT></A>）。除非特别说明，文中的例子都取自运行在四处理器 AIX 5.1 上的 Java 1.3.1 build ca131-20020706。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/i-gctroub/index.html#main"><B><FONT face=Verdana color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=N10063><SPAN class=atitle><STRONG><FONT size=4>堆管理概述</FONT></STRONG></SPAN></A></P>
<P>JVM 在初始化的过程中分配堆。堆的大小取决于指定或者默认的最小和最大值以及堆的使用情况。分配堆可能对可视化堆有所帮助，如<A href="http://www-128.ibm.com/developerworks/cn/java/i-gctroub/index.html#figure1"><FONT color=#996699>图 1</FONT></A> 所示，其中显示了 <I>heapbase</I>、<I>heaplimit</I> 和 <I>heaptop</I>。</P><BR><A name=figure1><B>图 1. 堆的概念视图</B></A><BR><IMG height=226 alt=堆的概念视图 src="http://www-128.ibm.com/developerworks/cn/java/i-gctroub/theheap.jpg" width=193> <BR>
<P>Heapbase 表示堆底，heaptop 则表示堆能够增长到的最大绝对值。差值（<CODE><FONT face=Courier size=2>heaptop - heapbase</FONT></CODE>）由命令行参数 <CODE><FONT face=Courier size=2>-Xmx</FONT></CODE> 决定。该参数和其他命令行参数都是在关于 <A href="http://www-106.ibm.com/developerworks/ibm/library/i-garbage3/" target=new><FONT color=#5c81a7>verbosegc 和命令行参数</FONT></A>的 developerWorks 文档中描述的。heaplimit 指针可以随着堆的扩展上升，随着堆的收缩下降。heaplimit 永远不能超过 heaptop，也不能低于使用 <CODE><FONT face=Courier size=2>-Xms</FONT></CODE> 指定的初始堆大小。任何时候堆的大小都是 <CODE><FONT face=Courier size=2>heaplimit - heapbase</FONT></CODE>。 </P>
<P>如果整个堆的自由空间比例低于 <CODE><FONT face=Courier size=2>-Xminf</FONT></CODE> 指定的值（<I>minf</I> 是最小自由空间），堆就会扩展。如果整个堆的自由空间比例高于 <CODE><FONT face=Courier size=2>-Xmaxf</FONT></CODE> 指定的值（<I>maxf</I> 是最大自由空间），堆就会收缩。<CODE><FONT face=Courier size=2>-Xminf</FONT></CODE> 和 <CODE><FONT face=Courier size=2>-Xmaxf</FONT></CODE> 的默认值分别是 0.3 和 0.6，因此 JVM 总是尝试将堆的自由空间比例维持在 30% 到 60% 之间。参数 <CODE><FONT face=Courier size=2>-Xmine</FONT></CODE>（<I>mine</I> 是最小扩展大小）和 <CODE><FONT face=Courier size=2>-Xmaxe</FONT></CODE>（<I>maxe</I> 是最大扩展大小）控制扩展的增量。这 4 个参数对固定大小的堆不起作用（用相等的 <CODE><FONT face=Courier size=2>-Xms</FONT></CODE> 和 <CODE><FONT face=Courier size=2>-Xmx</FONT></CODE> 值启动 JVM，这意味着 <CODE><FONT face=Courier size=2>HeapLimit = HeapTop</FONT></CODE>），因为固定大小的堆不能扩展或收缩。</P>
<P>当 Java 线程请求存储时，如果 JVM 不能分配足够的存储块来满足这个请求，则可以说出现了<I>分配失败</I>（AF）。这时候就不可避免要进行垃圾收集。垃圾收集包括收集所有“不可达的（unreachable）”引用，以便重用它们所占用的空间。垃圾收集由请求分配的线程执行，是一种 <I>Stop-The-World</I>（STW）机制；执行垃圾收集算法时，Java 应用程序的其他所有线程（除了<A href="http://www-128.ibm.com/developerworks/cn/java/i-gctroub/index.html#pvc"><FONT color=#996699>垃圾收集帮助器线程</FONT></A>之外）都被挂起。</P>
<P>IBM 实现使用称为 <I>mark-sweep-compact</I>（MSC）的垃圾收集算法，它是根据三个不同的阶段命名的。 
<DL>
<DT><B><B>标记</B> </B>
<DD>表和所有“可达的”或者活动的对象。这个阶段从确定“根”开始，比如线程栈上的对象、Java Native Interface（JNI）局部引用和全局引用等，然后沿着每个引用进行递归，直到所有的引用都做上标记。 
<DT><B><B>清理</B> </B>
<DD>清除所有已经分配但没有标记的对象，收回这些对象使用的空间。 
<DT><B><B>压缩</B> </B>
<DD>将活动对象移动到一起，去掉堆中的空洞（hole）和碎片。 </DD></DL>
<P></P>
<P>关于 MSC 算法的细节，请参阅<A href="http://www-128.ibm.com/developerworks/cn/java/i-gctroub/index.html#resources"><FONT color=#996699>参考资料</FONT></A>。</P>
<P><A name=pvc><SPAN class=smalltitle><STRONG>并行和并发</STRONG></SPAN></A></P>
<P>虽然垃圾收集本身采用 STW 机制，但最新的 IBM JVM 版本都在多处理器机器上使用多个“帮助器”线程，以便减少每个阶段所花费的时间。因此，在默认情况下，JVM 1.3.0 在标记阶段采用并行模式。JVM 1.3.1 在标记和清理阶段都采用并行模式，在标记阶段还支持一种可选的并发模式，可以使用命令行开关 <CODE><FONT face=Courier size=2>-Xgcpolicy:optavgpause</FONT></CODE> 切换。撰写本文时，最新版本的 JVM 1.4.0 中有一种递增压缩模式，它并行化了（parallelize）压缩阶段。讨论这些模式时，重要的是要理解并行和并发的区别。</P>
<P>在拥有 <CODE><FONT face=Courier size=2>N</FONT></CODE> 个 CPU 的多处理器系统中，支持并行模式的 JVM 在初始化的时候会启动 <CODE><FONT face=Courier size=2>N-1</FONT></CODE> 个垃圾收集帮助器线程。运行应用程序代码的时候，这些线程一直处于空闲状态，只有当启动垃圾收集时才调用它们。在某一特定的阶段，会将一些工作分配给驱动垃圾收集的线程和帮助器线程，因此一共有 <CODE><FONT face=Courier size=2>N</FONT></CODE> 线程并行地运行在 <CODE><FONT face=Courier size=2>N</FONT></CODE>-CPU 机器上。如果要禁止并行模式，惟一的方法是使用 <CODE><FONT face=Courier size=2>-Xgcthreads</FONT></CODE> 参数改变启动的垃圾收集帮助器线程个数。</P>
<P>采用并发模式时，JVM 会启动一个后台线程（不利于垃圾收集帮助器线程），在执行应用程序线程的同时，部分工作是在后台完成的。后台线程会试着与应用程序并发执行，以完成垃圾收集的所有操作，因此在执行垃圾收集时，可以减少 STW 造成的暂停。但在某些情况下，并发处理可能对性能造成<A href="http://www-128.ibm.com/developerworks/cn/java/i-gctroub/index.html#concurrentmark"><FONT color=#996699>负面影响</FONT></A>，特别是对于 CPU 敏感的应用程序。</P>
<P>下表按照 JVM 版本列出了垃圾收集各个阶段的处理类型。</P>
<TABLE cellSpacing=0 cellPadding=3 width="90%" border=1>
<TBODY>
<TR>
<TD>&nbsp;</TD>
<TD><B>标记</B> </TD>
<TD><B>清理</B> </TD>
<TD><B>压缩</B> </TD></TR>
<TR>
<TD><B>IBM JVM 1.2.2</B> </TD>
<TD>X</TD>
<TD>X</TD>
<TD>X</TD></TR>
<TR>
<TD><B>IBM JVM 1.3.0</B> </TD>
<TD>P</TD>
<TD>X</TD>
<TD>X</TD></TR>
<TR>
<TD><B>IBM JVM 1.3.1</B> </TD>
<TD>P, C</TD>
<TD>P</TD>
<TD>X</TD></TR>
<TR>
<TD><B>IBM JVM 1.4.0</B> </TD>
<TD>P, C</TD>
<TD>P</TD>
<TD>P</TD></TR></TBODY></TABLE>
<P>其中： 
<DL compact>
<DT><B><B>X</B> </B>
<DD>单线程操作。 
<DT><B><B>P</B> </B>
<DD>并行操作（垃圾收集期间所有帮助器线程都在工作）。 
<DT><B><B>C</B> </B>
<DD>并发操作（后台线程和应用程序线程并发操作）。 </DD></DL>
<P></P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/i-gctroub/index.html#main"><B><FONT face=Verdana color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=N101C2><SPAN class=atitle><STRONG><FONT size=4>分析 verbosegc 输出</FONT></STRONG></SPAN></A></P>
<P>虽然也有分析程序和其他第三方工具，但本文仅讨论对 verbosegc 日志的分析。这些日志由 JVM 在指定 -verbosegc 命令行参数时生成，是一种非常可靠的独立于平台的调试工具。要获得完整的 verbosegc 语法，请参阅“<A href="http://www-106.ibm.com/developerworks/ibm/library/i-garbage3/" target=new><FONT color=#5c81a7>verbosegc and command-line parameters</FONT></A>”。</P>
<P>启用 verbosegc 可能对应用程序的性能有一定影响。如果这种影响是无法接受的，则应该使用测试系统来收集 verbosegc 日志。服务器应用程序通常一直使 verbosegc 处于激活状态。这是监控整个 JVM 是否运转良好的一种好办法，在出现 OutOfMemory 错误的情况下，这种方法具有无可估计的价值。</P>
<P>为了有效地分析 verbosegc 记录，必须把精力集中在相关信息上，并过滤掉“噪音”。通过编写脚本从很长的 verbosegc 追踪记录中提取信息并不难，但是这些记录的格式可能（而且通常确实如此）随不同的 JVM 版本而异。下面的例子用粗体或蓝色字体表示重要的信息。即使记录的格式看起来相差很大，也很容易在 verbosegc 日志中找到这些信息。</P>
<P><A name=N101D6><SPAN class=smalltitle><STRONG>您刷新的了吗？</STRONG></SPAN></A></P>
<P>在尝试本文中的建议之前，强烈建议您升级到最新的 JVM 服务刷新（SR）。每次进行新的服务刷新都会有很多修正和改进，应用新的服务刷新可以提高 JVM 的性能和稳定性。迁移到最新的版本（比如 JVM 1.4.0 或 1.3.1，根据使用的平台）提供了增强的性能特性。一定要为 JVM 安装所有必需的 OS 补丁（比如 AIX 上的维护级别）。这些信息记录在随 SDK/JRE 提供的 readme 文件中。</P>
<P><A name=N101DF><SPAN class=smalltitle><STRONG>正确设置堆的大小</STRONG></SPAN></A></P>
<P>计算正确的堆大小参数很容易，但它可能对应用程序启动时间和运行时性能有很大的影响。初始大小和最大值分别由参数 <CODE><FONT face=Courier size=2>-Xms</FONT></CODE> 和 <CODE><FONT face=Courier size=2>-Xmx</FONT></CODE> 控制，这些值通常是根据理想情况和重负荷情况下堆的使用情况的估计来设置的，但 verbosegc 可以帮助确定这些值，而避免胡乱猜测。下面是从启动到完成程序的初始化（或者进入“就绪”状态）这段时间里，一个应用程序的 verbosegc 输出，如下所示。</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">			
&lt;GC[0]: Expanded System Heap by 65536 bytes
&lt;GC[0]: Expanded System Heap by 65536 bytes

&lt;AF[1]: Allocation Failure. need 64 bytes, 0 ms since last AF&gt;
&lt;AF[1]: managing allocation failure, action=1 (</FONT><SPAN class=bboldcode><STRONG><FONT color=#3c5f84>0/3983128</FONT></STRONG></SPAN><FONT face="Lucida Console">) (209640/209640)&gt;
&lt;GC(1): GC cycle started Tue Oct 29 11:05:04 2002
&lt;GC(1): freed 1244912 bytes, </FONT><SPAN class=bboldcode><STRONG><FONT color=#3c5f84>34%</FONT></STRONG></SPAN><FONT face="Lucida Console"> free (1454552/4192768), in 10 ms&gt;
&lt;GC(1): mark: 9 ms, sweep: 1 ms, compact: 0 ms&gt;
&lt;GC(1): refs: soft 0 (age &gt;= 32), weak 5, final 237, phantom 0&gt;
&lt;AF[1]: completed in 12 ms&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>上述记录表明，第一次发生 AF 时，堆中的自由空间为 0%（3983128 中有 0 字节可用）。此外，第一次垃圾收集之后，自由空间比例上升到 34%，略高于 <CODE><FONT face=Courier size=2>-Xminf</FONT></CODE> 标记（默认为 30%）。根据应用程序的使用，使用 <CODE><FONT face=Courier size=2>-Xms</FONT></CODE> 分配更大的初始堆可能会更好一些。几乎可以肯定的是，上例中的应用程序在下一次 AF 时会导致堆扩展。分配更大的初始堆可以避免这种情况。一旦应用程序进入 Ready 状态，通常不会再遇到 AF，因此也就确定了比较好的初始堆大小。类似地，通过增加应用程序负载也可以探测到避免出现 OutOfMemory 错误的 <CODE><FONT face=Courier size=2>-Xmx</FONT></CODE> 值。</P>
<P>如果堆太小，即使应用程序不会长期使用很多对象，也会频繁地进行垃圾收集。因此，自然会出现使用很大的堆的倾向。但是由于平台和其他方面的因素，堆的最大大小还受物理因素的限制。如果堆被分页，性能就会急剧恶化，因此堆的大小一定不能超出安装在系统上的物理内存总量。比如，如果 AIX 机器上有 1 GB 的内存，就不应该为 Java 应用程序分配 2 GB 的堆。</P>
<P>即使应用程序在拥有 64 GB 内存的 p690 超级计算机上运行，也不一定就能使用 <CODE><FONT face=Courier size=2>-Xmx60g</FONT></CODE>（当然是 64 位的 JVM）。虽然在很长时间内，应用程序可能不会遇到 AF，但一旦发生 AF，STW 造成的停顿将很难应付。下面的记录取自 32 GB AIX 系统上分配了 20 GB 堆空间的 64 位 JVM 1.3.1（build <CODE><FONT face=Courier size=2>caix64131-20021102</FONT></CODE>），它展示了大型堆在这方面的影响。</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;AF[29]: Allocation Failure. need 2321688 bytes, 88925 ms since last AF&gt;
&lt;AF[29]: managing allocation failure, action=1 (3235443800/20968372736) 
   (3145728/3145728)&gt;
&lt;GC(29): GC cycle started Mon Nov 4 14:46:20 2002
&lt;GC(29): freed 8838057824 bytes, 57% free (12076647352/20971518464), 
   in </FONT><SPAN class=bboldcode><STRONG><FONT color=#3c5f84>4749 ms</FONT></STRONG></SPAN><FONT face="Lucida Console">&gt;
&lt;GC(29): mark: 4240 ms, sweep: 509 ms, compact: 0 ms&gt;
&lt;GC(29): refs: soft 0 (age &gt;= 32), weak 0, final 1, phantom 0&gt;
&lt;AF[29]: completed in </FONT><SPAN class=bboldcode><STRONG><FONT color=#3c5f84>4763 ms</FONT></STRONG></SPAN><FONT face="Lucida Console">&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>垃圾收集用了将近五秒钟，还不包括压缩！垃圾收集周期所花费的时间直接与堆的大小成正比。一条好的原则是根据需要设置堆的大小，而不是将它配置得太大或太小。</P>
<P>常见的一种性能优化技术是将初始堆大小（<CODE><FONT face=Courier size=2>-Xms</FONT></CODE>）设成与最大堆大小（<CODE><FONT face=Courier size=2>-Xmx</FONT></CODE>）相同。因为不会出现堆扩展和堆收缩，所以在某些情况下，这样做可以显著地改善性能。通常，只有需要处理大量分配请求的应用程序时，才在初始和最大堆大小之间设置较大的差值。但是要记住，如果指定 <CODE><FONT face=Courier size=2>-Xms100m -Xmx100m</FONT></CODE>，那么 JVM 将在整个生命期中消耗 100 MB 的内存，即使利用率不超过 10%。</P>
<P>另一方面，也可以使用 <CODE><FONT face=Courier size=2>-Xinitsh</FONT></CODE> 在开始的时候分配较大的系统堆，从而避免出现 <A href="http://www-128.ibm.com/developerworks/cn/java/i-gctroub/index.html#esh"><FONT color=#996699>Expanded System Heap</FONT></A> 消息。但这些消息完全可以忽略。系统堆随着需要而扩展，并且永远不会发生垃圾收集，它只包含那些度过了 JVM 实例整个生命期的对象。</P>
<P><A name=N10244><SPAN class=smalltitle><STRONG>避免堆失效</STRONG></SPAN></A></P>
<P>如果使用大小可变的堆（比如，<CODE><FONT face=Courier size=2>-Xms</FONT></CODE> 和 <CODE><FONT face=Courier size=2>-Xmx</FONT></CODE> 不同），应用程序可能遇到这样的情况，不断出现分配失败而堆没有扩展。这就是<I>堆失效</I>，是由于堆的大小刚刚能够避免扩展但又不足以解决以后的分配失败而造成的。通常，垃圾收集周期释放的空间不仅可以满足当前的分配失败，而且还有很多可供以后的分配请求使用的空间。但是，如果堆处于失效状态，那么每个垃圾收集周期释放的空间刚刚能够满足当前的分配失败。结果，下一次分配请求时，又会进入垃圾收集周期，依此类推。大量生存时间很短的对象也可能造成这种现象。</P>
<P>避免这种循环的一种办法是增加 <CODE><FONT face=Courier size=2>-Xminf</FONT></CODE> 和 <CODE><FONT face=Courier size=2>-Xmaxf</FONT></CODE> 的值。比方说，如果使用 <CODE><FONT face=Courier size=2>-Xminf.5</FONT></CODE>，堆将增长到至少有 50% 的自由空间。同样，增加 <CODE><FONT face=Courier size=2>-Xmaxf</FONT></CODE> 也是很合理。如果 <CODE><FONT face=Courier size=2>-Xminf.5</FONT></CODE> 等于 5，<CODE><FONT face=Courier size=2>-Xmaxf</FONT></CODE> 为默认值 0.6，因为 JVM 要把自由空间比例保持在 50% 和 60% 之间，所以就会出现太多的扩展和收缩。两者相差 0.3 是一个不错的选择，这样 <CODE><FONT face=Courier size=2>-Xmaxf.8</FONT></CODE> 可以很好地匹配 <CODE><FONT face=Courier size=2>-Xminf.5</FONT></CODE>。</P>
<P>如果记录表明，需要多次扩展才能达到稳定的堆大小，但可以更改 <CODE><FONT face=Courier size=2>-Xmine</FONT></CODE>，根据应用程序的行为来设置扩展大小的最小值。目标是获得足够的可用空间，不仅能满足当前的请求，而且能满足以后的很多请求，从而避免过多的垃圾收集周期。<CODE><FONT face=Courier size=2>-Xmine</FONT></CODE>、<CODE><FONT face=Courier size=2>-Xmaxf</FONT></CODE> 和 <CODE><FONT face=Courier size=2>-Xminf</FONT></CODE> 为控制应用程序的内存使用特性提供了很大的灵活性。</P>
<P><A name=N1028E><SPAN class=smalltitle><STRONG>标记栈溢出</STRONG></SPAN></A></P>
<P>使用 verbosegc 最重要的一项检查是没有出现“mark stack overflow”消息。下面的记录显示了这种消息及其影响。</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;AF[50]: Allocation Failure. need 272 bytes, 18097 ms since last AF&gt;
&lt;AF[50]: managing allocation failure, action=1 (0/190698952) 
   (9584432/10036784)&gt;
&lt;GC(111): </FONT><SPAN class=bboldcode><STRONG><FONT color=#3c5f84>mark stack overflow</FONT></STRONG></SPAN><FONT face="Lucida Console">&gt;
&lt;GC(111): freed 77795928 bytes in 1041 ms, 43% free (87380360/200735736)&gt;
&lt;GC(111): </FONT><SPAN class=bboldcode><STRONG><FONT color=#3c5f84>mark: 949 ms</FONT></STRONG></SPAN><FONT face="Lucida Console">, sweep: 92 ms, compact: 0 ms&gt;
&lt;GC(111): refs: soft 0 (age &gt;= 32), weak 0, final 0, phantom 0&gt;
&lt;AF[50]: completed in 1042 ms&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>在垃圾收集的标记阶段，如果引用的个数造成 JVM 内部的“标记栈”溢出，就会引发这种消息。在标记阶段，垃圾收集处理代码使用这个栈压入所有已知的引用，以便递归扫描每个活动引用。溢出的原因是堆中的活动对象过多（或者更准确地说，对象嵌套过深），这通常表明应用程序代码存在缺陷。除非能够通过外部设置控制应用程序中的活动对象个数（比如某种对象池），那么需要在应用程序源代码中解决这个问题。建议使用分析工具确定活动的引用。</P>
<P>如果不能避免大量的活动引用，<A href="http://www-128.ibm.com/developerworks/cn/java/i-gctroub/index.html#concurrentmark"><FONT color=#996699>并发标记</FONT></A>可能是一种可行的选择。</P>
<P><A name=N102AE><SPAN class=smalltitle><STRONG>摆脱 finalizer</STRONG></SPAN></A></P>
<P>下面的记录显示了一种有趣的情况：解决分配失败花费了 2.78 秒钟，其中还不包括压缩所用的时间。</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;AF[1]: Allocation Failure. need 56 bytes, 0 ms since last AF&gt;
&lt;AF[1]: managing allocation failure, action=1 (0/521140736) 
   (3145728/3145728)&gt;
&lt;GC(1): GC cycle started Thu Aug 29 19:25:45 2002
&lt;GC(1): freed 321890808 bytes, 61% free (325036536/524286464), in 2776 ms&gt;
&lt;GC(1): mark: 2672 ms, sweep: 104 ms, compact: 0 ms&gt;
&lt;GC(1): refs: soft 0 (age &gt;= 32), weak 11, </FONT><SPAN class=bboldcode><STRONG><FONT color=#3c5f84>final 549708</FONT></STRONG></SPAN><FONT face="Lucida Console">, phantom 0&gt;
&lt;AF[1]: </FONT><SPAN class=bboldcode><STRONG><FONT color=#3c5f84>completed in 2780 ms</FONT></STRONG></SPAN><FONT face="Lucida Console">&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>罪魁祸首是必须被结束掉的对象数量。无论如何，使用 finalizer 不是一个好主意，虽然在特定的情况下这是不可避免的，但是应该仅仅将它作为完成其他方法不能实现的操作的不得已方法。比方说，无论如何都要避免在 finalizer 内部执行分配。</P>
<P><A name=N102C7><SPAN class=smalltitle><STRONG>避免非常大的分配</STRONG></SPAN></A></P>
<P>有时候问题不是由当时的堆状态造成的，而是因为分配失败造成的。比如：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;AF[212]: Allocation Failure. need </FONT><SPAN class=bboldcode><STRONG><FONT color=#3c5f84>912920 bytes</FONT></STRONG></SPAN><FONT face="Lucida Console">, 34284 ms since last AF&gt;
&lt;AF[212]: managing allocation failure, action=2 (117758504/261028856)&gt;
&lt;GC(273): freed 65646648 bytes in 2100 ms, 70% free (183405152/261028856)&gt;
&lt;GC(273): mark: 425 ms, sweep: 89 ms, </FONT><SPAN class=bboldcode><STRONG><FONT color=#3c5f84>compact: 1586 ms</FONT></STRONG></SPAN><FONT face="Lucida Console">&gt;
&lt;GC(273): refs: soft 0 (age &gt;= 32), weak 0, final 0, phantom 0&gt;
&lt;GC(273): moved 755766 objects, 43253888 bytes, <SPAN class=rboldcode><STRONG><FONT color=#ff0000>reason=0</FONT></STRONG></SPAN>, used x4C0 more 
   bytes&gt;
&lt;AF[212]: completed in 2101 ms&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>这些记录来自一个非常老的 JVM（准确地说是 <CODE><FONT face=Courier size=2>ca130-20010615</FONT></CODE>），因此压缩的原因（红色显示）显示为 0。但是压缩 256 MB 的堆花费了 1.5 秒！为何这么差？再来看一看最初的请求，最初请求的是 912920 字节 —— 将近 1 MB。</P>
<P>分配的内存块都必须是连续的，而随着堆越来越满，找到较大的连续块越来越困难。这不仅仅是 Java 的问题，使用 C 中的 malloc 也会遇到这个问题。JVM 在压缩阶段通过重新分配引用来减少碎片，但其代价是要冻结应用程序较长的时间。上面的记录表明已经完成了压缩阶段，分配一大块空间的总时间超过了 2秒。</P>
<P>下面的记录说明了最糟糕的一种情况。</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;AF[370]: Allocation Failure. need </FONT><SPAN class=bboldcode><STRONG><FONT color=#3c5f84>2241056 bytes</FONT></STRONG></SPAN><FONT face="Lucida Console">, 613 ms since last AF&gt;
&lt;AF[370]: managing allocation failure, action=2 </FONT><SPAN class=bboldcode><STRONG><FONT color=#3c5f84>(135487112/1291844600)</FONT></STRONG></SPAN><FONT face="Lucida Console">&gt;
&lt;GC: Wed Oct 16 10:16:46 2002
&lt;GC(455): freed 41815176 bytes in 28663 ms, 13% free (177302288/1291844600)&gt;
&lt;GC(455): mark: 3233 ms, sweep: 328 ms, compact: 25102 ms&gt;
&lt;GC(455): refs: soft 0 (age &gt;= 32), weak 0, final 17, phantom 0&gt;
&lt;GC(455): moved 15822115 objects, 615093008 bytes, reason=1, used x698 
   more bytes&gt;
&lt;AF[370]: managing allocation failure, action=3 (177302288/1291844600)&gt;
&lt;AF[370]: managing allocation failure, action=4 (177302288/1291844600)&gt;
&lt;AF[370]: clearing all remaining soft refs&gt;
&lt;GC(456): freed 176216 bytes in 3532 ms, 13% free (177478504/1291844600)&gt;
&lt;GC(456): mark: 3215 ms, sweep: 317 ms, compact: 0 ms&gt;
&lt;GC(456): refs: soft 16 (age &gt;= 32), weak 0, final 0, phantom 0&gt;
&lt;GC(457): freed 9592 bytes in 23781 ms, 13% free (177488096/1291844600)&gt;
&lt;GC(457): mark: 3231 ms, sweep: 315 ms, compact: 20235 ms&gt;
&lt;GC(457): refs: soft 0 (age &gt;= 32), weak 0, final 0, phantom 0&gt;
&lt;GC(457): moved 2794668 objects, 110333360 bytes, reason=1, used x30 more 
   bytes&gt;
&lt;AF[370]: managing allocation failure, action=5 (177488096/1291844600)&gt;
&lt;AF[370]: </FONT><SPAN class=bboldcode><STRONG><FONT color=#3c5f84>totally out of heap space</FONT></STRONG></SPAN><FONT face="Lucida Console">&gt;
&lt;AF[370]: </FONT><SPAN class=bboldcode><STRONG><FONT color=#3c5f84>completed in 268307 ms</FONT></STRONG></SPAN><FONT face="Lucida Console">&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>请求的是一个 2 MB 的对象（2241056 bytes），虽然在 1.2 GB 的堆（1291844600）中有 135 MB (135487112) 自由空间，但却不能分配一个 2 MB 的块。虽然进行了一切可能的搜索，花费了 268 秒，但仍然没有找到足够大的块。而且还出现了糟糕的“堆空间严重不足”消息，指出 JVM 的内存不足。</P>
<P>最好的办法是：如果可能的话，把分配请求分解成较小的块。这样，较大的堆空间可能会起作用，但多数情况下，这样做只是推迟了问题出现的时间。</P>
<P><A name=N10309><SPAN class=smalltitle><STRONG>碎片及其成因</STRONG></SPAN></A></P>
<P>我们再看一看上例中的其中一行：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;GC(455): freed 41815176 bytes in 28663 ms, </FONT><SPAN class=bboldcode><STRONG><FONT color=#3c5f84>13% free 
   (177302288/1291844600)</FONT></STRONG></SPAN><FONT face="Lucida Console">&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>虽然有 177 MB 的自由空间，却不能分配 2 MB 的块。原因在于：虽然垃圾收集周期可以压缩堆中的孔洞，但是堆中有些内容不能在压缩过程中重新分配。比如，应用程序可能使用 JNI 分配和引用对象或数组。这些分配在内存中是固定的，既不能被重新分配，也不能被回收，除非使用适当的 JNI 调用来释放它们。IBM Java 服务团队可以帮助确定这类引用，在这种情况下，分析工具也大有用武之地。</P>
<P>类似地，因为类块是在堆的外部引用的，因此也是固定的。即使没有固定的对象，大的分配一般也会导致出现碎片。所幸的是，这类严重的碎片很少出现。</P>
<P><A name=concurrentmark><SPAN class=smalltitle><STRONG>需要并发标记吗？</STRONG></SPAN></A></P>
<P>如果由于垃圾收集造成 Java 应用程序不时地停顿，并发标记可以帮助减少停顿的时间，使应用程序运行更平稳。但有时候，并发标记可能会降低应用程序的吞吐能力。建议分别使用和禁止并发标记，使用相同的负荷来测量对应用程序性能的影响，并加以比较。</P>
<P>但是，观察并发标记运行的 verbosegc 输出可能提供大量关于加速的信息。不需要分析打印出来的记录的每一部分，有意义的部分包括并发标记能够成功扫描的概率（EXHAUSTED 和 ABORTED/HALTED），以及后台线程能够做多少工作。</P>
<P>下面的三个记录属于同一个 Java 应用程序，是在一次运行中的不同阶段创建的，它们说明了并发运行的三种不同结果。</P>
<P>第一种可能的结果是并发标记得到 EXHAUSTED：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;CON[3]: Concurrent collection, (3457752/533723648) (3145728/3145728), 
   23273 ms since last CON&gt;
&lt;GC(246): Concurrent </FONT><SPAN class=bboldcode><STRONG><FONT color=#3c5f84>EXHAUSTED</FONT></STRONG></SPAN><FONT face="Lucida Console"> by Background helper . Target=82856559 
   Traced=57287216 (3324474+53962742) Free=3457752&gt;
&lt;GC(246): Cards cleaning Done. cleaned:13668 (33 skipped). Initial count 
   13701 (Factor 0.142)&gt;
&lt;GC(246): GC cycle started Tue Oct 1 00:05:56 2002
&lt;GC(246): freed 436622248 bytes, 82% free (443225728/536869376), in 218 ms&gt;
&lt;GC(246): </FONT><SPAN class=bboldcode><STRONG><FONT color=#3c5f84>mark: 51 ms</FONT></STRONG></SPAN><FONT face="Lucida Console">, sweep: 167 ms, compact: 0 ms&gt;
&lt;GC(246): In mark: Final dirty Cards scan: 43 ms 158 cards (total:127 ms)
&lt;GC(246): refs: soft 0 (age &gt;= 32), weak 0, final 5, phantom 0&gt;
&lt;CON[3]: completed in </FONT><SPAN class=bboldcode><STRONG><FONT color=#3c5f84>230 ms</FONT></STRONG></SPAN><FONT face="Lucida Console">&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>这表明并发标记按照我们所预期的那样工作。EXHAUSTED 意味着后台线程能够在出现分配失败之前完成自己的工作。因为后台线程扫描了 3324474 个字节（而应用程序线程扫描了 53962742 个字节），后台线程能够获得足够的 CPU 时间来减少总的标记时间。因此，STW 中的标记阶段只用了 51 毫秒（ms），总的 STW 时间也不过 230 毫秒。这对于 512 MB 的堆来说，这已经很不错了。</P>
<P>下面是 ABORTED 并发标记运行：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;AF[164]: Allocation Failure. need 962336 bytes, 75323 ms since last AF&gt;
&lt;AF[164]: managing allocation failure, action=1 (83408328/533723648) 
   (3145728/3145728)&gt;
&lt;GC(247): Concurrent </FONT><SPAN class=bboldcode><STRONG><FONT color=#3c5f84>ABORTED</FONT></STRONG></SPAN><FONT face="Lucida Console">. Target=84703195 Traced=0 (0+0) Free=83408328&gt;
&lt;GC(247): GC cycle started Tue Oct 1 00:06:22 2002
&lt;GC(247): freed 350077400 bytes, 81% free (436631456/536869376), in 896 ms&gt;
&lt;GC(247): </FONT><SPAN class=bboldcode><STRONG><FONT color=#3c5f84>mark: 695 ms</FONT></STRONG></SPAN><FONT face="Lucida Console">, sweep: 201 ms, compact: 0 ms&gt;
&lt;GC(247): refs: soft 0 (age &gt;= 32), weak 0, final 7, phantom 0&gt;
&lt;AF[164]: completed in </FONT><SPAN class=bboldcode><STRONG><FONT color=#3c5f84>912 ms</FONT></STRONG></SPAN><FONT face="Lucida Console">&gt;
&lt;CONCURRENT GC Free= 11530600 Expected free space= 11526488 Kickoff=11530370&gt;
&lt; Initial Trace rate is 8.00&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>这是最糟糕的情况。并发标记被终止，主要是因为要分配大型对象和调用了 System.gc()。如果应用程序频繁地这样做，那么就不能从并发标记中获得好处。</P>
<P>最好是 HALTED 并发标记：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;AF[168]: Allocation Failure. need 139280 bytes, 25520 ms since last AF&gt;
&lt;AF[168]: managing allocation failure, action=1 (11204296/533723648) 
   (3145728/3145728)&gt;
&lt;GC(251): Concurrent </FONT><SPAN class=bboldcode><STRONG><FONT color=#3c5f84>HALTED</FONT></STRONG></SPAN><FONT face="Lucida Console"> (state=64). Target=118320773 Traced=35469830 
   (14765196+20704634) Free=11204296&gt;
&lt;GC(251): No Dirty Cards cleaned (Factor 0.177)&gt;
&lt;GC(251): GC cycle started Tue Oct 1 00:08:06 2002
&lt;GC(251): freed 385174400 bytes, 74% free (399524424/536869376), in 389 ms&gt;
&lt;GC(251): </FONT><SPAN class=bboldcode><STRONG><FONT color=#3c5f84>mark: 274 ms</FONT></STRONG></SPAN><FONT face="Lucida Console">, sweep: 115 ms, compact: 0 ms&gt;
&lt;GC(251): In mark: Final dirty Cards scan: 46 ms 2619 cards (total:225 ms)
&lt;GC(251): refs: soft 0 (age &gt;= 32), weak 0, final 6, phantom 0&gt;
&lt;AF[168]: completed in </FONT><SPAN class=bboldcode><STRONG><FONT color=#3c5f84>414 ms</FONT></STRONG></SPAN><FONT face="Lucida Console">&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>从并发标记的应用来看，HALTED 介于 EXHAUSTED 和 ABORTED 之间，它表明只完成了部分工作。上面的记录说明，在进行下一次分配失败之前，没有完成扫描。在垃圾收集周期中，标记阶段花费了 274 毫秒，总的时间上升到 414 毫秒。</P>
<P>在理想的情况下，多数垃圾收集周期都并发收集（由于并发标记完成其工作而触发，标记为 EXHAUSTED），而不是出现分配失败。如果应用程序调用 System.gc()，记录中会出现很多 ABORTED 行。</P>
<P>在多数应用程序中，并发标记都可以改善性能，对于“标记栈溢出”也有所帮助。但是，如果标记栈溢出是由于缺陷造成的，惟一的解决办法就是修正缺陷。</P>
<P><A name=N1037B><SPAN class=smalltitle><STRONG>应该避免的开关</STRONG></SPAN></A></P>
<P>下列命令行开关应<I>避免</I> 使用。</P>
<TABLE cellSpacing=0 cellPadding=3 width="90%" border=1>
<TBODY>
<TR vAlign=top>
<TD><B>命令行开关</B> </TD>
<TD><B>说明</B> </TD></TR>
<TR vAlign=top>
<TD><CODE><FONT face=Courier size=2>-Xnocompactgc</FONT></CODE> </TD>
<TD>该参数完全关闭压缩。虽然在性能方面有短期的好处，最终应用程序堆将变得支离破碎，即使堆中有足够的自由空间也会导致 OutOfMemory 错误</TD></TR>
<TR vAlign=top>
<TD><CODE><FONT face=Courier size=2>-Xcompactgc</FONT></CODE> </TD>
<TD>使用该参数将导致每个垃圾收集周期都执行压缩，无论是否有必要。JVM 在压缩时要做大量的决策，在普通模式下会推迟压缩</TD></TR>
<TR vAlign=top>
<TD><CODE><FONT face=Courier size=2>-Xgcthreads</FONT></CODE> </TD>
<TD>该参数控制 JVM 在启动过程中创建的垃圾收集帮助器线程个数。对于 N-处理器机器，默认的线程数为 N-1。这些线程提供并行标记和并行清理模式中的并行机制</TD></TR></TBODY></TABLE><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/i-gctroub/index.html#main"><B><FONT face=Verdana color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=N103C8><SPAN class=atitle><STRONG><FONT size=4>结束语</FONT></STRONG></SPAN></A></P>
<P>本文简单介绍了 IBM JVM 的垃圾收集和堆管理能力。以后的 verbosegc 日志很可能提供更多有用的信息。</P>
<P>总结一下文中提出的建议：</P>
<UL>
<LI>只要可能就升级到最新的 JVM 版本。您遇到的错误可能已经被发现并解决了。 
<LI>调整 <CODE><FONT face=Courier size=2>-Xms</FONT></CODE>、<CODE><FONT face=Courier size=2>-Xmx</FONT></CODE> 和 <CODE><FONT face=Courier size=2>-Xminf</FONT></CODE>，直到 verbosegc 输出给出分配失败数量与每次垃圾收集造成的停顿数量之间的一个可接受平衡。使用固定大小的堆避免收缩或扩展。 
<LI>如果可能的话，将较大的（&gt;500 KB）块分解成更小的块。 
<LI>不要忽略“标记栈溢出”消息。 
<LI>避免使用 finalizer。 
<LI>试一试并发标记。 
<LI>问问是否有必要调用 System.gc()，如果没有必要则删除它。 </LI></UL>
<P>如您所见，这个话题可不是三言两语就能说明白的。但是，只需要打一个电话或者发一封电子邮件，就能与您的好伙伴 IBM Technical Support Team 取得联系（<A href="http://www-128.ibm.com/developerworks/cn/java/i-gctroub/index.html#resources"><FONT color=#996699>参考资料</FONT></A>中的链接是很好的起点）。对于您遇到的特殊情况，他们要比任何文章都更清楚。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/i-gctroub/index.html#main"><B><FONT face=Verdana color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=resources><SPAN class=atitle><STRONG><FONT size=4>参考资料 </FONT></STRONG></SPAN></A></P>
<UL>
<LI>您可以参阅本文在 developerWorks 全球站点上的 <A href="http://www.ibm.com/developerworks/library/i-gctroub/" target=_blank><FONT color=#5c81a7>英文原文</FONT></A>。<BR><BR>
<LI>Sam Borman 关于 IBM JVM Storage 组件的系列文章是 IBM Java 垃圾收集实现的最详尽的参考资料： 
<UL>
<LI>“<A href="http://www-106.ibm.com/developerworks/ibm/library/i-garbage1/"><FONT color=#5c81a7>Sensible sanitation: Understanding the IBM Java Garbage Collector, Part 1: Object Allocation</FONT></A>”（<I>developerWorks</I>，2002 年 8 月） 
<LI>“<A href="http://www-106.ibm.com/developerworks/ibm/library/i-garbage2/"><FONT color=#5c81a7>Sensible sanitation: Understanding the IBM Java Garbage Collector, Part 2: Garbage Collection</FONT></A>”（<I>developerWorks</I>，2002 年 8 月） 
<LI>“<A href="http://www-106.ibm.com/developerworks/ibm/library/i-garbage3/"><FONT color=#5c81a7>Sensible sanitation: Understanding the IBM Java Garbage Collector, Part 3: verbosegc and command-line parameters</FONT></A>”（<I>developerWorks</I>，2002 年 9 月） </LI></UL><BR>
<LI>请访问 <A href="http://www.s390.ibm.com/Java"><FONT color=#5c81a7>Java 2 on the OS/390 and z/OS Platforms</FONT></A> 站点，关于可重置 JVM 的信息，请参阅 <I><A href="http://www-1.ibm.com/servers/eserver/zseries/software/java/pdf/jtc0a100.pdf"><FONT color=#5c81a7>New IBM Technology featuring Persistent Reusable Java Virtual Machines</FONT></A> </I>(PDF)。<BR><BR>
<LI>artima.com 的 <A href="http://www.artima.com/insidejvm/ed2/ch09GarbageCollection01.html"><FONT color=#5c81a7>Inside Java 2 Virtual Machine</FONT></A> 中，“<A href="http://www.artima.com/insidejvm/applets/HeapOfFish.html"><FONT color=#5c81a7>Heap of Fish</FONT></A>”对 Mark-Sweep-Compact 算法作了很好的介绍。<BR><BR>
<LI>如果需要帮助的话，请访问 <A href="http://www-1.ibm.com/servers/eserver/techsupport.html"><FONT color=#5c81a7>IBM eServer 技术支持</FONT></A>页面。<BR><BR>
<LI>关于 IBM 服务器的应用方面，可以在 <A href="http://www-1.ibm.com/servers/esdd/"><FONT color=#5c81a7>IBM eServer Developer Domain</FONT></A> 上可以找到更多技术文章。<BR><BR>
<LI>developerWorks <A href="http://www.ibm.com/developerworks/cn/java/"><FONT color=#996699>Java 技术专区</FONT></A>有数百篇的技术文章和教程。<BR></LI></UL><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/i-gctroub/index.html#main"><B><FONT face=Verdana color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=author><SPAN class=atitle><STRONG><FONT size=4>关于作者</FONT></STRONG></SPAN></A></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD colSpan=2><STRONG><FONT size=4><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width="100%"></FONT></STRONG></TD></TR>
<TR vAlign=top align=left>
<TD>
<P><STRONG><FONT size=4><IMG height=80 alt=author src="http://www-106.ibm.com/developerworks/i/p-formag.jpg" width=64 align=left border=0></FONT></STRONG></P></TD>
<TD>
<P>Sumit Chawla 在 IBM eServer 部门为 ISV 提供 Java 支持。您可以通过 <A href="mailto:sumitc@us.ibm.com"><FONT color=#5c81a7>sumitc@us.ibm.com</FONT></A> 和他联系。</P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/jacky/aggbug/25873.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jacky/" target="_blank">jacky</a> 2005-12-29 13:58 <a href="http://www.blogjava.net/jacky/articles/25873.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关注性能: 调优垃圾收集[转]</title><link>http://www.blogjava.net/jacky/articles/25872.html</link><dc:creator>jacky</dc:creator><author>jacky</author><pubDate>Thu, 29 Dec 2005 05:56:00 GMT</pubDate><guid>http://www.blogjava.net/jacky/articles/25872.html</guid><wfw:comment>http://www.blogjava.net/jacky/comments/25872.html</wfw:comment><comments>http://www.blogjava.net/jacky/articles/25872.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jacky/comments/commentRss/25872.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jacky/services/trackbacks/25872.html</trackback:ping><description><![CDATA[<P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width="100%">
<H1><SPAN style="COLOR: #999999">关注性能: </SPAN>调优垃圾收集</H1>
<P id=subtitle>将 100 MB 的垃圾打包成 50 MB 的包</P><IMG class=display-img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=1></TD>
<TD class=no-print width=192><IMG height=18 alt=developerWorks src="http://www-128.ibm.com/developerworks/i/dw.gif" width=192></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD width="100%">
<TABLE class=no-print cellSpacing=0 cellPadding=0 width=160 align=right border=0>
<TBODY>
<TR>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD>
<TABLE cellSpacing=0 cellPadding=0 width=150 border=0>
<TBODY>
<TR>
<TD class=v14-header-1-small>文档选项</TD></TR></TBODY></TABLE>
<TABLE class=v14-gray-table-border cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=no-padding width=150>
<TABLE cellSpacing=0 cellPadding=0 width=143 border=0><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8> 
<FORM name=email action=https://www-130.ibm.com/developerworks/secure/email-it.jsp><INPUT type=hidden value="目前许多人都热衷于写网志，随着博客和浏览者人数的增加，许多网志站点都经历了增长的烦恼。在调优过载的网志服务器时，Kirt 和 Jack 通过远程控制，为网志服务器加入了强大的优化程序。" name=body><INPUT type=hidden value="关注性能: 调优垃圾收集" name=subject><INPUT type=hidden value=cn name=lang>
<SCRIPT language=JavaScript type=text/javascript>
<!--
document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt=""/></td><td width="16"><img src="//www.ibm.com/i/v14/icons/em.gif" height="16" width="16" vspace="3" alt="将此页作为电子邮件发送" /></td><td width="122"><p><a class="smallplainlink" href="javascript:document.email.submit();"><b>将此页作为电子邮件发送</b></a></p></td></tr>');
//-->
</SCRIPT>
 
<TBODY>
<TR vAlign=top>
<TD width=8><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></TD>
<TD width=16><IMG height=16 alt=将此页作为电子邮件发送 src="http://www.ibm.com/i/v14/icons/em.gif" width=16 vspace=3></TD>
<TD width=122>
<P><A class=smallplainlink href="javascript:document.email.submit();"><B><FONT face=Verdana color=#5c81a7 size=2>将此页作为电子邮件发送</FONT></B></A></P></TD></TR><NOSCRIPT><tr valign="top"><td width="8"><img alt="" height="1" width="8" src="//www.ibm.com/i/c.gif"/></td><td width="16"><img alt="" width="16" height="16" src="//www.ibm.com/i/c.gif"/></td><td class="small" width="122"><p><span class="ast">未显示需要 JavaScript 的文档选项</span></p></td></tr></NOSCRIPT></FORM></TBODY></TABLE></TD></TR></TBODY></TABLE><BR>
<TABLE cellSpacing=0 cellPadding=0 width=150 border=0>
<TBODY>
<TR>
<TD class=v14-header-1-small>对此页的评价</TD></TR></TBODY></TABLE>
<TABLE class=v14-gray-table-border cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=no-padding width=150>
<TABLE cellSpacing=0 cellPadding=0 width=143 border=0>
<TBODY>
<TR vAlign=top>
<TD width=8><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></TD>
<TD><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/d_bold.gif" width=16 vspace=3 border=0></TD>
<TD width=125>
<P><A class=smallplainlink href="http://www-128.ibm.com/developerworks/cn/java/j-perf06304/?ca=dwcn-newsletter-java#rate"><B><FONT face=Verdana color=#996699 size=2>帮助我们改进这些内容</FONT></B></A></P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR></TD></TR></TBODY></TABLE>
<P>级别: 初级</P>
<P><A href="http://www-128.ibm.com/developerworks/cn/java/j-perf06304/?ca=dwcn-newsletter-java#author"><FONT color=#996699>Jack Shirazi</FONT></A>, 董事, JavaPerformanceTuning.com<BR><A href="http://www-128.ibm.com/developerworks/cn/java/j-perf06304/?ca=dwcn-newsletter-java#author"><FONT color=#996699>Kirk Pepperdine</FONT></A>, CTO, JavaPerformanceTuning.com<BR></P>
<P>2004 年 7 月 30 日</P>
<BLOCKQUOTE>如果您是当前写网志（blogging）狂热者中的一员，则可能听说过 Blog-City，这是由苏格兰的一家小公司 Blog-City Ltd. 拥有和运营的网志站点。当一些意料之外的性能问题突然出现时，Java 性能专家 Jack Shirazi 和 Kirk Pepperdine 被邀请帮助进行 Blog-City 的技术调整。他们的检测工作因为受硬件约束和整个项目所使用的通信通道（IRC、ftp 和 偶尔的电子邮件）的限制而变得复杂。</BLOCKQUOTE>
<P>随着网志作为公共日记的流行，网志主机迅速地增长。所以对于 Blog-City 的人来说，非常清楚他们的站点需要发展和提高。为了满足其增长的需要，该公司最近刚刚推出了 Blog-City version 2.0。正像经常出现的情况那样，当新的应用程序转入运行阶段时，由于各种原因，其性能无法完全满足期望的要求，突然出现随机的长时间应用程序被挂起的现象还不是最坏的情况。</P>
<P>在其核心，Blog-City 依靠 Blue Dragon Servlet 引擎（CFML 引擎）和数据库。令人惊讶的是，所有这些软件都宿主在运行 Red Hat Linux 的相当老的 P3 机器上。这台机器具有单个硬盘和 512MB 内存，这对于过去的负载来说是足够强大的，但它正在承受不断增长的负载。Blog-City 的运作方式很成功，但其资源限制却成了其成功路上的绊脚石。尽管如此，这就是未来还要继续使用一段时间的所有硬件。</P>
<P><A name=1.0><SPAN class=atitle><STRONG><FONT size=4>问题定义</FONT></STRONG></SPAN></A></P>
<P>整个过程的第一步是确定突然出现应用程序减慢的原因。首先我们怀疑的对象是垃圾收集。正如我们在 <A href="http://www-128.ibm.com/developerworks/cn/java/j-perf05214"><FONT color=#5c81a7>本专栏的上月文章</FONT></A> 中所论述的那样，确定垃圾收集和内存利用问题是否对应用程序产生负面影响的最容易的方式是，设置 <CODE><FONT face=Courier size=2>-verbose:gc</FONT></CODE> JVM 选项，并检查日志输出。因此我们重新启动应用程序，打开冗长的垃圾收集日志选项，然后耐心地等待应用程序的性能降低。我们的耐心换来的是非常详细的垃圾收集日志文件。 </P>
<P>从对日志文件的最初分析中看，在这一应用程序中垃圾收集的瓶颈是显而易见的。种种迹象包括垃圾收集的频率、持续时间和总体效率都已表明这一点。高于普通垃圾收集频率的常见原因是，堆的大小刚好足以适应所有当前正在使用的运行对象，无法适应新的正被创建的对象。虽然应用程序消耗大量堆可能有许多原因，但主要原因可能是没有足够内存而导致垃圾收集器运行，因为它设法满足当前需要。换句话说，应用程序试图分配新对象，但失败了，如果失败的话，将触发垃圾收集程序。如果垃圾收集失败而无法恢复足够内存，它将迫使另一个花费更大的垃圾收集程序发生。即使 GC 恢复了足够的空间来满足瞬间需求，可以肯定的是，在应用程序程序另一次分配失败，触发另一个 GC 之前，时间不会很长。因此，应该关注重复扫描空闲堆空间的无效任务，而不是服务于应用程序的 JVM。</P>
<P>应用程序逐步消耗所有可用的堆空间可能有许多原因，但如果有更多内存的话，临时解决方案就是配置更大的堆。假设应用程序没有内存泄漏（或者也就是我们常说的“无意识地保留对象”），它将找到一个“自然”级别的堆消耗，在这个级别中，GC 将能够很适应地得到维持（除非对象创建的速度过快，以至 GC 总是处于赛跑状态）。在这种情况下，以及无意识地保留对象的情况下，我们需要对应用程序做一些变动，以便获得某些改进。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-perf06304/?ca=dwcn-newsletter-java#main"><B><FONT face=Verdana color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=2.0><SPAN class=atitle><STRONG><FONT size=4>如果仅仅是这样，那就太简单了</FONT></STRONG></SPAN></A></P>
<P>遗憾的是，我们必须面对严酷的现实因素——正在运行的机器只有 512 MB 内存。更糟的是，我们必须与数据库和其他运行在机器中的进程共享该空间。要完整理解这一点为什么至关重要，首先您必须明确理解垃圾收集的基本知识，以及它如何与底层操作系统进行交互。</P>
<P><A name=2.1><SPAN class=smalltitle><STRONG>虚拟内存不再是灵丹妙药</STRONG></SPAN></A></P>
<P>操作系统已经使用虚拟内存许多年了。正如您所知道的，虚拟内存使操作系统的内存看起来比实际的内存要多，这允许计算机运行那些所需内存比可用物理内存更大的程序，不使用内存的应用程序部分将保存在磁盘上。为了进一步简化，操作系统同时按页管理内存。页通常包含 512 字节到 8 KB，所有页的组合就组成了一个虚拟地址空间。操作系统维持一个页表，用于告诉操作系统如何映射虚拟地址到物理地址。当应用程序要求某个内存位置的内容时，操作系统（或硬件）将识别包含虚拟地址的页面。然后确定该页面是否在内存中，如果不在，将会报告 <I>页面错误</I>。但是有许多种方式来处理页面错误，最终的结果是，页面必须从磁盘载入到内存中。这样应用程序就可以访问到有效虚拟地址的内容。 </P>
<P>如果相关对象总是在内存的同一页面上聚合，那么 GC 的连续工作很可能出现困难。但是现实世界中，相关对象很少（如果有的话）出现聚合现象。实际结果是，依靠虚拟内存的系统将导致操作系统将页从内存中换入和换出，因为它标记然后废弃堆空间，而当聚合现象发生时，GC 将很多时间花在等待页面从磁盘换入而不是实际恢复内存上。因此，应用程序正在等待 GC，而 GC 正在等待磁盘，其间未完成任何真正的工作。由于本系统只有一个磁盘，并且它还需要支持数据库，因此我们在解决问题时处于两难境地。一方面，我们需要增加内存数量，这样我们可以减少 GC 的频率，但另一方面，我们还需要确保数据库的完好运行，而数据库也是内存的消耗大户。因此，我们需要了解应用程序所需的最小内存数量。</P>
<P>正如我们在上月看到的，在冗长的 GC 日志中这一信息可以很容易得到，无需为这一信息而扫描整个日志，我们使用免费的 JTune 工具（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-perf06304/?ca=dwcn-newsletter-java#resources"><FONT color=#996699>参考资料</FONT></A>）来解释冗长的 GC 日志。图 1 显示了经过垃圾收集之后的内存利用情况，其中我们将 <CODE><FONT face=Courier size=2>-Xmx</FONT></CODE> 设置为 256 MB。 </P><BR><A name=fig1><B>图 1. 垃圾收集之后的内存利用情况</B></A><BR><IMG height=287 alt="图 1. 垃圾收集之后的内存利用情况" src="http://www-128.ibm.com/developerworks/cn/java/j-perf06304/figure1.jpg" width=600> <BR><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-perf06304/?ca=dwcn-newsletter-java#main"><B><FONT face=Verdana color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=3.0><SPAN class=atitle><STRONG><FONT size=4>分析 verbose:gc 输出</FONT></STRONG></SPAN></A></P>
<P>在图 1 中，蓝色部分表示部分 GC。橙色区域表示完整的 GC，而粉色矩形表示两个完整 GC 在它们之间少于一毫秒之内已经发生的堆利用情况。从结果中我们看到，平均每 0.257 秒有 12,823 次清除。总共有 345 次完整的垃圾收集