﻿<?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 次完整的垃圾收集和 44 次紧挨着的垃圾收集。完整垃圾收集的平均持续时间是 7.303 秒，结果表明有 9.36% 的运行时间花费在垃圾收集程序上。虽然这个值偏高，它仍然保持在 10% 的正常水平之内。因此，在本例中，GC 是系统的繁重负担但还没有达到严重的地步。真正的问题是存在内存泄漏，这一点可以从总体上堆利用率不断增长的趋势看出来。</P>
<P>即使内存泄漏消耗了 50 MB 内存，它也应该是经过很长一段时间后才发生，这使得内存泄漏在较短的测试中很少会引人注意。内存泄漏的实际结果是，它把 JVM 的内存消耗推动到某个点，在该点它强迫 JVM （从而强迫操作系统）消耗内存，它强迫启动分页。图 2 就证明了这一点。注意正好在 55,000 秒标记之后，每一 GC 周期的持续时间中内存消耗突然地持续增加。</P><BR><A name=fig2><B>图 2. GC 持续时间</B></A><BR><IMG height=316 alt="图 2. GC 持续时间" src="http://www-128.ibm.com/developerworks/cn/java/j-perf06304/figure2.jpg" width=600> <BR>
<P>如您所想，由于垃圾收集的阻塞将导致系统只有更少的时间来分配给用户线程，因此用户响应开始增加。在日志的过去 10,000 秒中，我们看到每次完全收集（总共 15 次）花费时间超过了 30 秒，平均持续时间大约 70 秒 —— 这导致超过 10% 的处理时间分配给完全 GC。部分收集（这里刚好超过了 1000 次）无法正常工作，平均每次请求耗时 1.24 秒，远高于以前 11,800 次清除中的平均 0.25 秒。</P>
<P><A name=3.1><SPAN class=smalltitle><STRONG>分代收集</STRONG></SPAN></A></P>
<P>本文不涉及太深的细节（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-perf06304/?ca=dwcn-newsletter-java#resources"><FONT color=#996699>参考资料</FONT></A>，获取分代 GC 的详细描述），分代堆空间产生了“年轻”和“年老”对象，它们位于分开的堆空间中。在本配置中，年轻和年老分代空间可以通过不同的 GC 算法和策略来维持，以提高 GC 的整体性能。 </P>
<P>一种这样的策略是，进一步将年轻分代划分为创建空间，称为 Eden，以及残存（survivor）空间，用于幸存一个或者多个收集的年轻对象。如果在 Eden 中有足够的内存来适应新对象创建的话，这一般能工作正常。如果不是这种情况，那么对象可以在年老对象空间中创建。同样，如果残存空间足够的话，那么对象将移入年老分代空间。我们将使用这些事实来帮助调优遇到的问题。</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=4.0><SPAN class=atitle><STRONG><FONT size=4>减少完全收集的次数</FONT></STRONG></SPAN></A></P>
<P>Blog-City 所碰到的难题是在某一随机点出现长的暂停时间。一旦应用程序启动出现问题，不重新启动机器的话，就无法返回跟踪。由于长时间暂停的现象直接与长的 GC 相关，我们考虑如果将对象保持在年轻分代来减少完全 GC 的次数。由于完全 GC 的代价如此之大，在年轻分代收集更多对象能够得到更短的暂停时间。要完成这一任务，我们调整了一些垃圾收集参数，包括 <I>残存比率（survivor ratio）</I>和 <I>期限阈值（tenuring threshold）</I>。 </P>
<P>残存比率用于设置与年轻分代空间总体大小相关的残存空间的大小。如果残存比率设置为 8（Intel 的默认值），那么每一残存空间将是 Eden 空间的 1/8 大小。另一种考察它的方式是，年轻分代将该 Eden 空间划分为 10 个相同大小的值，该 Eden 将分配其中的 8 个，每一个残存空间的大小为 1。</P>
<P>我们的假设是，通过减少残存比率，我们可以减少由于残存空间中空间的缺乏，对象过早地被提升为年老分代的几率。另一种方法是增加期限阈值，这样的话，对象在提升之前将需要保留更多的 GC 事件。本着这个想法，Blog-City 将设置更改为 <CODE><FONT face=Courier size=2>-XX:SurvivorRatio=4</FONT></CODE> ，然后重新启动。 </P>
<P><A name=4.1><SPAN class=smalltitle><STRONG>选择低暂停时间的垃圾收集算法</STRONG></SPAN></A></P>
<P>由于这次技术调优的目标之一是减少暂停时间，我们决定抛弃默认的单线程、标记清扫的垃圾收集程序。我们选择通过标志 <CODE><FONT face=Courier size=2>XX:+UseParallelGC</FONT></CODE> 来采用并行拷贝收集程序。同样，有关实际算法的细节可以在 <A href="http://www-128.ibm.com/developerworks/cn/java/j-perf06304/?ca=dwcn-newsletter-java#resources"><FONT color=#996699>Resources</FONT></A>中找到，这里需要提一下的是，这一标志调用了一个多线程收集程序。线程的数量设置为 CPU 的数量。基于这一事实，了解为什么单线程并行拷贝垃圾收集程序要比传统的标记清扫算法工作更好是很困难的，但是从实际观察中可以体会到提供了某些性能上的优势。 </P>
<P>图 3 和图 4 的输出展示了使用标志 <CODE><FONT face=Courier size=2>-XX:SurvivorRatio=4 +XX:+UseParallelGC -server -Xmx256M</FONT></CODE> 运行时的结果。 </P><BR><A name=fig3><B>图 3. 新配置下的内存使用情况</B></A><BR><IMG height=302 alt="图 3. 新配置下的内存使用情况" src="http://www-128.ibm.com/developerworks/cn/java/j-perf06304/figure3.jpg" width=600> <BR>
<P>结果图表显示了明显的不同。虽然仍然有一个内存漏洞。内存消耗的总数相比前一个图已经是大大降低了。GC 持续时间的快速比较揭示了年轻分代和年老分代的总体 GC 持续时间的明显减少。</P><BR><A name=fig4><B>图 4. 新配置下的 GC 持续时间</B></A><BR><IMG height=318 alt="图 4. 新配置下的 GC 持续时间" src="http://www-128.ibm.com/developerworks/cn/java/j-perf06304/figure4.jpg" width=600> <BR>
<P><A name=N1013C><SPAN class=smalltitle><STRONG>有意的、无意的对象保持</STRONG></SPAN></A></P>
<P>由于应用程序是依靠内存的，跟踪内存泄漏并消除它们已经变得越来越重要。在本例中，用于支持缓存策略的组件决定了主要漏洞的来源。从最后的内存分析情况来看（图 3），虽然消除了主要内存泄漏，我们可以看到仍有另一个“低级别的”的漏洞，但这个漏洞比较小，因此它在下一版本发布之前可以忽略。</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=5.0><SPAN class=atitle><STRONG><FONT size=4>结束语</FONT></STRONG></SPAN></A></P>
<P>本文提出了许多挑战。首先，我们正在调优一个现实中的应用程序，这意味着更改会受到很多限制。第二个挑战是，这项任务是使用 IRC 聊天室远程操控的。聊天室不提供任何级别或质量的相互通信，而通信在这种类型的任务中往往是必需的。在本例中，团队已经习惯了聊天室的真实性，并能通过这种真实性毫无任何阻碍地工作着。</P>
<P>最后也是最困难的挑战是我们受硬件的限制。由于多种原因，我们不可能为系统添加新硬件。其中最大的问题是系统中物理内存的数量，而 JVM 和 MySQL 需要大量的内存。但是，通过系统地逐一应用许多更改，并度量它们对系统产生的影响，我们可以逐步地改进总体系统性能。</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=resources><SPAN class=atitle><STRONG><FONT size=4>参考资料 </FONT></STRONG></SPAN></A></P>
<UL>
<LI>阅读 Jack Shirazi 和 Kirk Pepperdine 的完整的 <A href="http://www.ibm.com/developerworks/views/java/articles.jsp?sort_order=desc&amp;expand=&amp;sort_by=Date&amp;show_abstract=true&amp;view_by=Search&amp;search_by=performance%3A"><FONT color=#996699><I>关注性能</I>系列文章 </FONT></A>。 <BR><BR>
<LI>文章“ <A href="http://www.ibm.com/developerworks/linux/library/l-mem26/"><FONT color=#5c81a7>Kernel comparison: Improved memory management in the 2.6 kernel</FONT></A>”考察了 IBM Linux 内核的虚拟内存（ <I>developerWorks</I>，2004 年 3 月）。 <BR><BR>
<LI>请访问 <A href="http://www.blog-city.com/"><FONT color=#5c81a7>Blog-City</FONT></A>。 <BR><BR>
<LI>下载 <A href="http://www.hp.com/products1/unix/java/java2/hpjtune/"><FONT color=#996699>HP JTune</FONT></A>。 <BR><BR>
<LI>阅读 Sam Borman 的“ <A href="http://www.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 月）。 <BR><BR>
<LI>在 <I>Java theory and practice</I>专栏中，Brian Goetz 在 <A href="http://www.ibm.com/developerworks/java/library/j-jtp01274.html"><FONT color=#5c81a7>Garbage collection and performance</FONT></A>（ <I>developerWorks</I>，2004 年 1 月）和 <A href="http://www-128.ibm.com/developerworks/cn/java/j-jtp11253/"><FONT color=#996699>Java 理论与实践：JVM 1.4.1 中的垃圾收集</FONT></A>（ <I>developerWorks</I>，2003 年 11 月）中考察了 GC。 <BR><BR>
<LI>有关 Sun VM 的权威指南，请阅读 <A href="http://java.sun.com/docs/hotspot/gc1.4.2/"><FONT color=#996699>Tuning Garbage Collection with the 1.4.2 Java[tm] Virtual Machine</FONT></A>。 <BR><BR>
<LI>在我们的网站上可以找到 <A href="http://www.javaperformancetuning.com/tips/index.shtml"><FONT color=#996699>GC 性能的技巧</FONT></A>。 <BR><BR>
<LI>从 <A href="http://www.amazon.com/exec/obidos/ASIN/0596003773/javaperforman-20/103-6446490-1420608"><FONT color=#5c81a7>JavaPerformance Tuning, 2nd Edition</FONT></A>一书中学习所有关于性能调优的知识。 <BR><BR>
<LI>请访问 <A href="http://devworks.krcinfo.com/"><FONT color=#5c81a7>Developer Bookstore</FONT></A>，获取技术书籍的完整列表，其中包括数百本 <A href="http://devworks.krcinfo.com/WebForms/ProductList.aspx?Search=Category&amp;id=1200"><FONT color=#5c81a7>Java 相关的图书</FONT></A>。 <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>是否对无需通常的高成本入口点（entry point ）或短期评估许可证的 IBM 测试产品感兴趣？ <A href="http://www-128.ibm.com/developerworks/cn/subscription/"><FONT color=#5c81a7>developerWorks Subscription</FONT></A>为 WebSphere ®、DB2 ®、Lotus ®、Rational ®和 Tivoli ®产品提供了低成本的 12 个月单用户许可证，包括基于 Eclipse 的 WebSphere Studio IDE，用于开发、测试、评估和展示您的应用程序。 <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-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=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>Jack Shirazi 是 <A href="http://www.javaperformancetuning.com/"><FONT color=#5c81a7>JavaPerformanceTuning.com</FONT></A>的董事和 <A href="http://devworks.krcinfo.com/WebForms/ProductDetails.aspx?ProductID=0596003773"><FONT color=#5c81a7><I>Java Performance Tuning, 2nd Edition</I> </FONT></A>(O'Reilly) 一书的作者。 </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>Kirk Pepperdine is 是 Java Performance Tuning.com 的首席技术官（Chief Technical Officer，CTO），过去 15 年来他一直专攻对象技术和性能调优。Kirk 是 <A href="http://devworks.krcinfo.com/WebForms/ProductDetails.aspx?ProductID=0672324261"><FONT color=#5c81a7><I>Ant Developer's Handbook</I> </FONT></A>(MacMillan) 一书的合著者。</P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE></P><img src ="http://www.blogjava.net/jacky/aggbug/25872.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:56 <a href="http://www.blogjava.net/jacky/articles/25872.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关注性能: 谈论垃圾[转]</title><link>http://www.blogjava.net/jacky/articles/25871.html</link><dc:creator>jacky</dc:creator><author>jacky</author><pubDate>Thu, 29 Dec 2005 05:55:00 GMT</pubDate><guid>http://www.blogjava.net/jacky/articles/25871.html</guid><wfw:comment>http://www.blogjava.net/jacky/comments/25871.html</wfw:comment><comments>http://www.blogjava.net/jacky/articles/25871.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jacky/comments/commentRss/25871.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jacky/services/trackbacks/25871.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">关注性能: </SPAN>谈论垃圾</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="您的应用程序是否经常出现 out-of-memory 错误？用户是否感受到响应时间有些不稳定？应用程序是否在相当长的时间内变得没有响应？应用程序的性能是否显得迟缓了？如果对任何一个问题的回答是肯定的，那么您很可能遇到了垃圾收集的问题了。先别进行优化，且听听 JavaPerformanceTuning.com 的 Jack Shirazi 和 Kirk Pepperdine 来解释如何识别垃圾收集问题，并由此帮助您回答这个问题：您知道垃圾收集器在干什么吗？" 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-perf05214/?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-perf05214/?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-perf05214/?ca=dwcn-newsletter-java#author"><FONT color=#996699>Kirk Pepperdine</FONT></A>, 首席技术官, JavaPerformanceTuning.com<BR></P>
<P>2004 年 5 月 01 日</P>
<BLOCKQUOTE>您的应用程序是否经常出现 out-of-memory 错误？用户是否感受到响应时间有些不稳定？应用程序是否在相当长的时间内变得没有响应？应用程序的性能是否显得迟缓了？如果对任何一个问题的回答是肯定的，那么您很可能遇到了垃圾收集的问题了。先别进行优化，且听听 JavaPerformanceTuning.com 的 Jack Shirazi 和 Kirk Pepperdine 来解释如何识别垃圾收集问题，并由此帮助您回答这个问题：您知道垃圾收集器在干什么吗？</BLOCKQUOTE>
<P>许多开发人员认为，内存管理至多是开发业务逻辑的主要任务之外的一项不重要的工作 —— 直到业务逻辑不能像预期的或者测试时那样执行得好。出现这种情况时，就需要知道哪里出错了及其原因，这意味着要理解应用程序如何与底层计算资源（特别是内存）进行交互。理解应用程序如何利用内存的最好方式是观察垃圾收集器的行动。</P>
<P><A name=1><SPAN class=atitle><STRONG><FONT size=4>为什么我的应用程序不连贯了？</FONT></STRONG></SPAN></A></P>
<P>Java 虚拟机中最大的一个性能问题是应用程序线程与同时运行的 GC 的互斥性。垃圾收集器要完成其工作，需要在一段时间内防止所有其他线程访问它正在处理的堆空间（内存）。按 GC 的术语，这段时间称为“stop-the-world”，并且，正如其名字所表明的，在垃圾收集器努力工作时，应用程序有一个急刹车。幸运的是，这种暂停通常是很短的，很难察觉到，但是很容易想像，如果应用程序在随机的时刻出现随机且较长时间的暂停，对应用程序的响应性和吞吐能力会有破坏性的影响。</P>
<P>不过 GC 只是应用程序出现不连贯和停顿的一个原因。那么如何确定 GC 对产生这些问题是否负有责任呢？要回答这个问题，我们需要测量垃圾收集器的工作强度，并当在系统中进行改变时继续这些测量，以定量地确定所做的改变是否有所期望的效果。</P>
<P><A name=1.1><SPAN class=smalltitle><STRONG>我需要多少内存？</STRONG></SPAN></A></P>
<P>普遍接受的信念是，在系统中添加内存将解决许多性能问题。虽然这个原则对于 JVM 来说经常是正确的，但是太多好东西可能对性能是有害的。因此技巧在于 Java 应用程序需要多少内存就给它多少，但是绝不多给。问题是，应用程序需要多少内存？对于应用程序不连贯的情况，我们需要观察垃圾收集行为以了解看它做的是否比所需要的更多。这些观察将告诉我们所做的改变是否有所期望的效果。</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-perf05214/?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><SPAN class=atitle><STRONG><FONT size=4>测量 GC 的活动</FONT></STRONG></SPAN></A></P>
<P>生成 GC 日志的标准方式是使用 <CODE><FONT face=Courier size=2>-verbose:gc</FONT></CODE> 旗标，设置这个旗标后，垃圾收集器会在每次运行时生成它所做的事情的汇总，一般是写入到控制台（通过标准输出或者标准错误）。许多 VM 支持一个允许 verbose GC 输出转向到一个文件的选项。例如，在 Sun 的 1.4 JVM 中，可以使用开关 <CODE><FONT face=Courier size=2>-Xloggc:filename</FONT></CODE> 将 GC 输出写到文件中。对于 HP JVM，要使用 <CODE><FONT face=Courier size=2>-Xverbosegc=file</FONT></CODE> 开关。在本文中，我们将分析 Sun 1.4.2 和 IBM 1.4.1 JVM 捕获的 verbose GC 输出。 </P>
<P>使用这个方法监视内存使用的一个最大好处是它对应用程序的性能的影响很小。不幸的是，这个解决方案并不完美，因为这些日志文件可能变得特别大，而维护它们可能需要重新启动 JVM。尽管如此，这种技术在生产环境中仍然是可行的，因为它可以帮助诊断只在这种环境中才列出的性能问题。</P>
<P><A name=2.1><SPAN class=smalltitle><STRONG>更深入观察 GC</STRONG></SPAN></A></P>
<P><CODE><FONT face=Courier size=2>-verbose:gc</FONT></CODE> 旗标生成的输出根据 JVM 厂商而不同，不同的垃圾收集器选项会报告特定于该实现的信息。例如，由 IBM JVM 生成的输出比由 Sun JVM 生成的输出冗长得多，而 Sun 的输出更适合于由工具读取。就是说，每一个 GC 日志传达基本信息 —— 使用了多少内存、恢复了多少内存、GC 周期用了多少时间，以及在收集期间是否采取了其他行动。从这些基本测量中，我们可以推断出有助于更好地理解所发生的事情的细节。我们要计算的统计如下所示： </P>
<UL>
<LI>考虑的运行时的持续时间 
<LI>收集总数 
<LI>收集频率 
<LI>收集所用最长时间 
<LI>收集所用总时间 
<LI>收集所用平均时间 
<LI>收集的平均间隔 
<LI>分配的字节总数 
<LI>每次收集每秒分配的字节数 
<LI>恢复的字节总数 
<LI>每次收集每秒恢复的字节总数 </LI></UL>
<P>理解了暂停时间，我们就可以理解 GC 对应用程序不响应是否负有部分或者全部责任了。一种实现这一任务的方法是将详细（verbose） GC 日志中的 GC 活动与系统采集的其他日志（如 Web 服务器日志中的请求 backlog）相对应。几乎可以肯定最长的 GC 暂停会导致整个系统响应可感觉的下降，所以知道什么时候响应下降是很有用的，这样就可以将 GC 活动与应用程序吞吐相关联起来。</P>
<P>另一种可能的竞争因素是堆内存分配和恢复的比率，称为 <I>churn</I>。产生大量立即释放的对象的应用程序通常会受到 churn 的拖累。更高的 churn 比率对垃圾收集器加以很大压力，创造了更多的内存资源竞争，这又可导致更长的暂停或者可怕的 <CODE><FONT face=Courier size=2>OutOfMemoryError</FONT></CODE> 。 </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-perf05214/?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><SPAN class=atitle><STRONG><FONT size=4>Sun GC 日志记录</FONT></STRONG></SPAN></A></P>
<P>清单 1 是由 Sun 1.4.2_03 JVM 以 <CODE><FONT face=Courier size=2>-Xloggc:filename</FONT></CODE> 运行默认的标记-清除收集器所生成的日志记录的例子。可以看到，日志项非常精确地记录了每次所做的事情。 </P><BR><A name=L1><B>清单 1. 使用 -Xloggc:filename 旗标的 GC 日志记录</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">69.713: [GC 11536K-&gt;11044K(12016K), 0.0032621 secs]
69.717: [Full GC 11044K-&gt;5143K(12016K), 0.1429698 secs]
69.865: [GC 5958K-&gt;5338K(11628K), 0.0021492 secs]
69.872: [GC 6169K-&gt;5418K(11628K), 0.0021718 secs]
69.878: [GC 6248K-&gt;5588K(11628K), 0.0029761 secs]
69.886: [GC 6404K-&gt;5657K(11628K), 0.0017877 secs]
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>首先注意到的可能是每一项日志记录是写在一组方括号内的。其他 GC 算法，如并发收集器， 可能将一些值分解为更细的信息。如果是这种情况，这些被分解的值会由包围在嵌入的一组方括号中的细节所替代，这使工具可以更容易地处理详细 GC 输出。</P>
<P>我们的研究首先从分析清单 1 中标记为 69.713 的记录开始。这个标记是 JVM 开始后的秒数和毫秒数的时间戳。在这个例子中，JVM 在这个 GC 周期开始之前运行了 69.713 秒。从左到右的字段为：执行的收集的类型、GC 之前的堆使用、总的堆能力和 GC 事件的持续时间。从这个描述中我们可以看出第一个 GC 事件是一个小的收集。在 GC 开始之前，使用了 11536 Kb 的堆空间。在完成时，使用了 11044 Kb，堆能力为 12016 Kb，而整个收集用了 .0032621 秒。下一个事件，一个完全的 GC，在 69.717 秒时或者上一个小 GC 事件之后 0.003 秒时开始。注意，如果将小 GC 事件的持续时间加到其开始时间上，就会看到它在完全的 GC 开始之前不到 1毫秒结束。因此我们可以得出结论：小收集没有恢复足够的空间，这种失败触发了完全的 GC。对应用程序来说，这像是一个持续了 0.1462319 秒的事件。让我们继续确定如何计算其他值。</P>
<P><A name=3.1><SPAN class=smalltitle><STRONG>GC 日志记录的参数确定</STRONG></SPAN></A></P>
<P>我们通过确定每个 GC 日志记录中的值的参数来开始分析：</P>
<BLOCKQUOTE>R(n) = T(n): [ &lt;GC&gt; HB-&gt;HE(HC), D]</BLOCKQUOTE>
<P>
<TABLE cellSpacing=0 cellPadding=3 width="70%" border=0>
<TBODY>
<TR>
<TD><CODE><FONT face=Courier size=2>n</FONT></CODE> </TD>
<TD>清单中记录的索引，1 是第一个，m 是最后一个</TD></TR>
<TR>
<TD><CODE><FONT face=Courier size=2>R(n)</FONT></CODE> </TD>
<TD>GC 记录</TD></TR>
<TR>
<TD><CODE><FONT face=Courier size=2>T(n)</FONT></CODE> </TD>
<TD>第 n 个 GC 发生的时间</TD></TR>
<TR>
<TD><CODE><FONT face=Courier size=2>HB</FONT></CODE> </TD>
<TD>GC 之前堆的数量</TD></TR>
<TR>
<TD><CODE><FONT face=Courier size=2>HE</FONT></CODE> </TD>
<TD>GC 之后使用的堆数量</TD></TR>
<TR>
<TD><CODE><FONT face=Courier size=2>HC</FONT></CODE> </TD>
<TD>堆空间的总量</TD></TR>
<TR>
<TD><CODE><FONT face=Courier size=2>D</FONT></CODE> </TD>
<TD>GC 周期的持续时间</TD></TR></TBODY></TABLE></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-perf05214/?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=4.0><SPAN class=atitle><STRONG><FONT size=4>基本值</FONT></STRONG></SPAN></A></P>
<P>我们要计算的第一个值是日志所覆盖的运行时整个持续时间。如果要考虑每一项记录，那么就要分析最后一项记录的时间戳。因为清单 1 只表示全部日志记录的一部分，我们需要从最后一项中提取出第一个时间戳。尽管对这个例子来说，这个数字足够精确，但是为了绝对准确，需要加上最后 GC 的持续时间。其原因是时间戳是在 GC 开始时记录的，而记录表示在记录了时间戳以后发生的事情。</P>
<P>剩余值是取记录中相应的值的总和计算的。值得注意的是恢复的字节可以通过分析记录中测量的关系而计算，而分配的字节可以通过分析前后记录测量之间的关系计算。例如，如果考虑在时间戳 69.872 和 69.878 之间发现的记录对，可以用第一个记录中 GC 之后占用的内存数量减去第二个记录在 GC 之前占用的字节数量计算在新的一代（generation）中分配的字节数量： <CODE><FONT face=Courier size=2>6248 Kb - 5418 Kb = 830 Kb</FONT></CODE> 。下面表 1 展示了其他值的公式。 </P>
<P>要找出最长的 GC 暂停，我们只需要查看持续时间并寻找 <CODE><FONT face=Courier size=2>D(n)</FONT></CODE> （记录 n 的持续时间）的最大值。 </P>
<P><A name=T1><SPAN class=smalltitle><STRONG>表 1. 统计公式</STRONG></SPAN></A></P>
<P>
<TABLE cellSpacing=0 cellPadding=3 width="70%" border=1>
<TBODY>
<TR>
<TD><STRONG>统计</STRONG> </TD>
<TD><B>计算（时间单位调整为秒）</B> </TD></TR>
<TR>
<TD>运行时持续时间</TD>
<TD><CODE><FONT face=Courier size=2>RT = (T(M) + D(M)) - T(1)</FONT></CODE> </TD></TR>
<TR>
<TD>小收集的总数</TD>
<TD><CODE><FONT face=Courier size=2>TMC = Sum( R(n))</FONT></CODE> 其中 <CODE><FONT face=Courier size=2>GC(n)</FONT></CODE> = <CODE><FONT face=Courier size=2>GC</FONT></CODE> </TD></TR>
<TR>
<TD>完全收集的总数</TD>
<TD><CODE><FONT face=Courier size=2>TFC = Sum( R(n))</FONT></CODE> 其中 <CODE><FONT face=Courier size=2>GC(n)</FONT></CODE> = <CODE><FONT face=Courier size=2>Full</FONT></CODE> </TD></TR>
<TR>
<TD>收集频率（小收集）</TD>
<TD><CODE><FONT face=Courier size=2>CFM = TMC / RT</FONT></CODE> </TD></TR>
<TR>
<TD>收集频率（完全）</TD>
<TD><CODE><FONT face=Courier size=2>CFF = TFC / RT</FONT></CODE> </TD></TR>
<TR>
<TD>收集的时间（最长的小收集）</TD>
<TD><CODE><FONT face=Courier size=2>MAX(D(n))</FONT></CODE> for all <CODE><FONT face=Courier size=2>n</FONT></CODE> 其中 <CODE><FONT face=Courier size=2>GC(n)</FONT></CODE> = <CODE><FONT face=Courier size=2>GC</FONT></CODE> </TD></TR>
<TR>
<TD>收集的时间（最长的完全收集）</TD>
<TD><CODE><FONT face=Courier size=2>MAX(D(n))</FONT></CODE> for all <CODE><FONT face=Courier size=2>n</FONT></CODE> 其中 <CODE><FONT face=Courier size=2>GC(n)</FONT></CODE> = <CODE><FONT face=Courier size=2>Full</FONT></CODE> </TD></TR>
<TR>
<TD>小收集的时间（总数）</TD>
<TD><CODE><FONT face=Courier size=2>TTMC = Sum(D(n))</FONT></CODE> for all <CODE><FONT face=Courier size=2>n</FONT></CODE> 其中 <CODE><FONT face=Courier size=2>GC(n)</FONT></CODE> = <CODE><FONT face=Courier size=2>GC</FONT></CODE> </TD></TR>
<TR>
<TD>完全收集的时间（总数）</TD>
<TD><CODE><FONT face=Courier size=2>TTFC Sum(D(n))</FONT></CODE> for all <CODE><FONT face=Courier size=2>n</FONT></CODE> 其中 <CODE><FONT face=Courier size=2>GC(n)</FONT></CODE> = <CODE><FONT face=Courier size=2>Full</FONT></CODE> </TD></TR>
<TR>
<TD>收集的时间（总数）</TD>
<TD><CODE><FONT face=Courier size=2>TTC = TTMC + TTFC</FONT></CODE> </TD></TR>
<TR>
<TD>小收集的时间（平均）</TD>
<TD><CODE><FONT face=Courier size=2>ATMC = TTMC / RT</FONT></CODE> </TD></TR>
<TR>
<TD>完全收集的时间（平均）</TD>
<TD><CODE><FONT face=Courier size=2>ATFC = TTFC / RT</FONT></CODE> </TD></TR>
<TR>
<TD>收集的时间（平均）</TD>
<TD><CODE><FONT face=Courier size=2>ATC = TTC / RT</FONT></CODE> </TD></TR>
<TR>
<TD>收集间隔（平均）</TD>
<TD><CODE><FONT face=Courier size=2>Sum( T(n+1) - T(n)) / (TMC + TFC)</FONT></CODE> for all <CODE><FONT face=Courier size=2>n</FONT></CODE> </TD></TR>
<TR>
<TD>分配的字节（总数）</TD>
<TD><CODE><FONT face=Courier size=2>TBA = Sum(HB(n+1) - HE(n))</FONT></CODE> 对于所有 <CODE><FONT face=Courier size=2>n</FONT></CODE> </TD></TR>
<TR>
<TD>分配的字节（每秒）</TD>
<TD><CODE><FONT face=Courier size=2>TBA / RT</FONT></CODE> </TD></TR>
<TR>
<TD>分配的字节（每次收集）</TD>
<TD><CODE><FONT face=Courier size=2>TBA / (TMC + TFC)</FONT></CODE> </TD></TR>
<TR>
<TD>小收集恢复的字节（总数）</TD>
<TD><CODE><FONT face=Courier size=2>BRM = Sum(HB(n) - HE(n))</FONT></CODE> 其中 <CODE><FONT face=Courier size=2>GC(n)</FONT></CODE> = <CODE><FONT face=Courier size=2>GC</FONT></CODE> </TD></TR>
<TR>
<TD>完全收集恢复的字节（总数）</TD>
<TD><CODE><FONT face=Courier size=2>BRF = Sum(HB(n) - HE(n))</FONT></CODE> 其中 <CODE><FONT face=Courier size=2>GC(n)</FONT></CODE> = <CODE><FONT face=Courier size=2>Full</FONT></CODE> </TD></TR>
<TR>
<TD>恢复的字节（总数）</TD>
<TD><CODE><FONT face=Courier size=2>BRT = BRM + BRF</FONT></CODE> </TD></TR>
<TR>
<TD>恢复的字节（每次小收集）</TD>
<TD><CODE><FONT face=Courier size=2>BRPM = BRM / TMC</FONT></CODE> </TD></TR>
<TR>
<TD>恢复的字节（每次完全收集）</TD>
<TD><CODE><FONT face=Courier size=2>BRPF = BRF / TMF</FONT></CODE> </TD></TR>
<TR>
<TD>恢复的字节（小收集每秒）</TD>
<TD><CODE><FONT face=Courier size=2>BRP = BRM / TTMC</FONT></CODE> </TD></TR>
<TR>
<TD>恢复的字节（完全收集每秒）</TD>
<TD><CODE><FONT face=Courier size=2>BRF = BRF / TTFC</FONT></CODE> </TD></TR></TBODY></TABLE></P>
<P>可以从公式中看出，我们经常需要分别考虑完全 GC 和小 GC。小 GC 与完全 GC 有根本性的不同，一般来说前者至少比后者要快一个数量级。我们可以通过快速分析清单 1 看出这一点 —— 最长的小收集比完全收集快 50 倍。</P>
<P>下面表 2 显示对清单 1 中的值使用表 1 中的公式的结果。</P>
<P><A name=T2><SPAN class=smalltitle><STRONG>表 2. Sun GC 日志分析</STRONG></SPAN></A></P>
<P>
<TABLE cellSpacing=0 cellPadding=3 width="70%" border=1>
<TBODY>
<TR>
<TD><STRONG>统计</STRONG> </TD>
<TD><B>计算（时间单位调整为秒）</B> </TD></TR>
<TR>
<TD>运行时持续时间</TD>
<TD>(69.886 + 0.0017877) - 69.713 = 0.1747877</TD></TR>
<TR>
<TD>小收集总数</TD>
<TD>5</TD></TR>
<TR>
<TD>完全收集总数</TD>
<TD>1</TD></TR>
<TR>
<TD>收集频率（小收集）</TD>
<TD>5 / 0.1747877 = 28.6 per second</TD></TR>
<TR>
<TD>收集频率（完全）</TD>
<TD>1 / 0.1747877 = 5.27 per second</TD></TR>
<TR>
<TD>收集时间（最长的小收集）</TD>
<TD>0.0032621</TD></TR>
<TR>
<TD>收集时间（最长的完全收集）</TD>
<TD>0.1429698</TD></TR>
<TR>
<TD>小收集的时间（总数）</TD>
<TD>0.0123469</TD></TR>
<TR>
<TD>完全收集的时间（总数）</TD>
<TD>0.1429698</TD></TR>
<TR>
<TD>收集的时间（总数）</TD>
<TD>0.1553167</TD></TR>
<TR>
<TD>小收集的时间（平均）</TD>
<TD>7.1%</TD></TR>
<TR>
<TD>完全收集的时间（平均）</TD>
<TD>81.8%</TD></TR>
<TR>
<TD>收集的时间（平均）</TD>
<TD>88.9%</TD></TR>
<TR>
<TD>收集间隔（平均）</TD>
<TD>.173/5=0.0346</TD></TR>
<TR>
<TD>分配的字节（总数）</TD>
<TD>3292</TD></TR>
<TR>
<TD>分配的字节（每秒）</TD>
<TD>18834 Kb/second</TD></TR>
<TR>
<TD>分配的字节（每次收集）</TD>
<TD>549 Kb</TD></TR>
<TR>
<TD>小收集恢复的字节（总数）</TD>
<TD>3270 Kb</TD></TR>
<TR>
<TD>完全收集恢复的字节（总数）</TD>
<TD>5901 Kb</TD></TR>
<TR>
<TD>恢复的字节（总数）</TD>
<TD>5901 + 3270 = 9171 Kb</TD></TR>
<TR>
<TD>恢复的字节（每次小收集）</TD>
<TD>3270/5 = 654</TD></TR>
<TR>
<TD>恢复的字节（每次完全收集）</TD>
<TD>5901/1 = 5901</TD></TR>
<TR>
<TD>恢复的字节（小收集每秒）</TD>
<TD>3270/0.0123469 = 264843 Kb/second</TD></TR>
<TR>
<TD>恢复的字节（完全收集每秒）</TD>
<TD>5901/0.1429698 = 41274K/second</TD></TR></TBODY></TABLE></P>
<P>表 2 包含从这些看来简单的日志中推算出的大量信息。取决于所关注的问题，可能不需要计算所有这些值，因为其中一些值比另一些更有用。对于应用程序长时间不响应的情况，要关注的是 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-perf05214/?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=5.0><SPAN class=atitle><STRONG><FONT size=4>IBM GC 日志记录</FONT></STRONG></SPAN></A></P>
<P>与 Sun 日志不同，IBM 日志特别冗长。即使这样，仍然需要一个指导以完全理解所提供的信息。清单 2 是这种 <CODE><FONT face=Courier size=2>verbose:gc</FONT></CODE> 日志文件的一部分。 </P><BR><A name=L2><B>清单 2. IBM JVM verbose:gc 输出</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;AF[31]: Allocation Failure. need 528 bytes, 969 ms since last AF&gt;
&lt;AF[31]: managing allocation failure, action=1 (0/97133320) (1082224/1824504)&gt;
  &lt;GC(31): GC cycle started Wed Feb 25 23:08:41 2004
  &lt;GC(31): freed 36259000 bytes, 37% free (37341224/98957824), in 569 ms&gt;
  &lt;GC(31): mark: 532 ms, sweep: 37 ms, compact: 0 ms&gt;
  &lt;GC(31): refs: soft 0 (age &gt;= 32), weak 0, final 2, phantom 0&gt;
&lt;AF[31]: managing allocation failure, action=3 (37341224/98957824)&gt;
  &lt;GC(31): need to expand mark bits for 116324864-byte heap&gt;
  &lt;GC(31): expanded mark bits by 270336 to 1818624 bytes&gt;
  &lt;GC(31): need to expand alloc bits for 116324864-byte heap&gt;
  &lt;GC(31): expanded alloc bits by 270336 to 1818624 bytes&gt;
  &lt;GC(31): need to expand FR bits for 116324864-byte heap&gt;
  &lt;GC(31): expanded FR bits by 544768 to 3637248 bytes&gt;
  &lt;GC(31): expanded heap by 17367040 to 116324864 bytes, 47% free, ratio:0.417&gt;
&lt;AF[31]: completed in 812 ms&gt;

&lt;AF[32]: Allocation Failure. need 528 bytes, 1765 ms since last AF&gt;
&lt;AF[32]: managing allocation failure, action=1 (0/115394264) (930600/930600)&gt;
  &lt;GC(32): GC cycle started Wed Feb 25 23:08:43 2004
  &lt;GC(32): freed 54489184 bytes, 47% free (55419784/116324864), in 326 ms&gt;
  &lt;GC(32): mark: 292 ms, sweep: 34 ms, compact: 0 ms&gt;
  &lt;GC(32): refs: soft 0 (age &gt;= 32), weak 0, final 0, phantom 0&gt;
&lt;AF[32]: completed in 328 ms&gt;

&lt;AF[33]: Allocation Failure. need 528 bytes, 1686 ms since last AF&gt;
&lt;AF[33]: managing allocation failure, action=1 (0/115510592) (814272/814272)&gt;
  &lt;GC(33): GC cycle started Wed Feb 25 23:08:45 2004
  &lt;GC(33): freed 56382392 bytes, 49% free (57196664/116324864), in 323 ms&gt;
  &lt;GC(33): mark: 285 ms, sweep: 38 ms, compact: 0 ms&gt;
  &lt;GC(33): refs: soft 0 (age &gt;= 32), weak 0, final 18, phantom 0&gt;
&lt;AF[33]: completed in 324 ms&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>清单 2 中有三项 GC 日志记录。我将不会提供完全的说明，而是推荐一篇由 Sam Borman 所写的很好的文章“Sensible Sanitation”（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-perf05214/?ca=dwcn-newsletter-java#resources"><FONT color=#996699>参考资料</FONT></A>）。对于我们的目的，需要与像对 Sun JVM 的日志那样推算出同样类型的信息。好的方面是有一些计算结果已经是现成的了。例如，如果分析 <CODE><FONT face=Courier size=2>AF[31]</FONT></CODE> （事件 31 分配失败），将会看到 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-perf05214/?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=6.0><SPAN class=atitle><STRONG><FONT size=4>这些数字有什么意义</FONT></STRONG></SPAN></A></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-perf05214/?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=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-perf05214.html"><FONT color=#5c81a7>英文原文</FONT></A>. <BR><BR>
<LI>查看 <A href="http://www.ibm.com/developerworks/views/java/articles.jsp?sort_order=desc&amp;expand=&amp;sort_by=Date&amp;show_abstract=true&amp;view_by=Search&amp;search_by=performance%3A"><FONT color=#996699><I>关注性能</I>系列的其他文章 </FONT></A>。 <BR><BR>
<LI>Sam Borman 的“ <A href="http://www.ibm.com/developerworks/ibm/library/i-garbage1/"><FONT color=#5c81a7>Sensible Sanitation</FONT></A>”讨论了 IBM JVM 垃圾收集器的细节（ <I>developerWorks</I>，2002 年 8 月）。 <BR><BR>
<LI>Brian Goetz 在 “ <A href="http://www.ibm.com/developerworks/java/library/j-jtp01274.html"><FONT color=#5c81a7>Java theory and practice: Garbage collection and performance</FONT></A>”（ <I>developerWorks</I>，2004 年 1 月）中讨论了 GC。 <BR><BR>
<LI>对于 Sun VM 的权威指导，请阅读 <A href="http://java.sun.com/docs/hotspot/gc1.4.2/"><FONT color=#996699>Tuning Garbage Collection with the 1.4.2 Java[tm] Virtual Machine</FONT></A>。 <BR><BR>
<LI>在 Java 性能调优网站找到 <A href="http://www.javaperformancetuning.com/tips/index.shtml"><FONT color=#996699>GC 性能提示</FONT></A>。 <BR><BR>
<LI>阅读 Jack Shirazi 的 <A href="http://devworks.krcinfo.com/WebForms/ProductDetails.aspx?ProductID=0596003773"><FONT color=#5c81a7><I>Java Performance Tuning, 2nd Edition</I> </FONT></A>（O'Reilly）中关于 GC 的所有内容。 <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>请访问 <A href="http://devworks.krcinfo.com/"><FONT color=#5c81a7>Developer Bookstore</FONT></A>，获得技术书籍的完整列表，其中包括数百本 <A href="http://devworks.krcinfo.com/WebForms/ProductList.aspx?Search=Category&amp;id=1200"><FONT color=#5c81a7>Java 相关主题</FONT></A>的书籍。 <BR><BR>
<LI>对于测试驱动 IBM 产品而不需要考虑高成本门槛或者短期的评估许可证感兴趣吗？ <A href="http://www-128.ibm.com/developerworks/cn/subscription/"><FONT color=#5c81a7>developerWorks Subscription</FONT></A>针对 WebSphere®、DB2®、Lotus®、Rational®以及 Tivoli®产品 —— 其中包括基于 Eclipse 的 WebSphere Studio IDE，提供一个低成本的、12 个月的单用户许可证，用于开发、测试、评估和演示您的应用程序。 <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-perf05214/?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=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>Jack Shirazi 是 <A href="http://www.javaperformancetuning.com/"><FONT color=#5c81a7>JavaPerformanceTuning.com</FONT></A>的董事，也是 <A href="http://devworks.krcinfo.com/WebForms/ProductDetails.aspx?ProductID=0596003773"><FONT color=#5c81a7><I>Java Performance Tuning, 2nd Edition</I> </FONT></A>（O'Reilly） 一书的作者。 </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>Kirk Pepperdine 是 Java Performance Tuning.com 的首席技术官，并且在过去 15 年一直关注对象技术和性能调优。Kirk 是 <A href="http://devworks.krcinfo.com/WebForms/ProductDetails.aspx?ProductID=0672324261"><FONT color=#5c81a7><I>Ant Developer's Handbook</I> </FONT></A>（MacMillan）一书的合著者。 </P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/jacky/aggbug/25871.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:55 <a href="http://www.blogjava.net/jacky/articles/25871.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>