﻿<?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-WOLF--执着-文章分类-java基础</title><link>http://www.blogjava.net/sutao/category/24291.html</link><description>用文字记录学习的体验！</description><language>zh-cn</language><lastBuildDate>Mon, 26 Nov 2007 04:09:14 GMT</lastBuildDate><pubDate>Mon, 26 Nov 2007 04:09:14 GMT</pubDate><ttl>60</ttl><item><title>高效java</title><link>http://www.blogjava.net/sutao/articles/162793.html</link><dc:creator>苏醄</dc:creator><author>苏醄</author><pubDate>Sat, 24 Nov 2007 02:24:00 GMT</pubDate><guid>http://www.blogjava.net/sutao/articles/162793.html</guid><wfw:comment>http://www.blogjava.net/sutao/comments/162793.html</wfw:comment><comments>http://www.blogjava.net/sutao/articles/162793.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sutao/comments/commentRss/162793.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sutao/services/trackbacks/162793.html</trackback:ping><description><![CDATA[<font style="color: #0000ff" size="4">每一种语言都有其自身的特点，只有掌握了其自身的特点，才能用它编写出高效的程序。下面就我个人实践所知谈谈javaSE方面的性能问题，<br />
javaEE方面的性能暂不讨论，要是时间可以再写一javaEE方面的性能问题的帖子。<br />
<br />
1， 尽量不要使用+号来连接字符串，至少不要在隔行中使用+来连接字符串。因为有的java虚拟机可能对字符串连接+做了性能优化，在都同行的+字符串连接，转化为StringBuffer的append()方法来连接，所以在同行使用+和使用StringBuffer的append 来做连接性能上差不多。<br />
2， 对小数据int的Integer封装，尽量的使用Integer.valueOf()创建，而不要使用new来创建。因为Integer类缓存了从-128到256个 状态的Integer。<br />
3， 对Boolean类，要用valueOf()或使用Boolean.TRUE或Boolean.FALSE来创建对象。我个人觉得对Boolean类用private构造函数，可能就会避免不好的使用Boolean类了。<br />
4， 在设计类时应尽可能地避免在类的默认构造函数中创建，初始化大量的对象。<br />
5， 合理的申请数组空间，如果数组中所保存的元素占用内存空间较大或数组本身长度较长的情况，我们釆用可以釆用软引用的技术来引用数组，以&#8220;提醒&#8221;JVM及时的回收垃圾内存，维护系统的稳定性。<br />
6，&nbsp; 避免创建重复的对象，我们在编写一个方法的时候应该先考虑方法里的局部对象域能否改为private static final，从而避免创建重复的对象。<br />
7, 把try/catch块放入循环体内，会极大的影响性能，如果编译JIT被关闭或者你所使用的一个不带JIT的JVM，性能会将下降21%之多!<br />
8，StringBuffer的构造器会创建一个默认大小(通常是16)的字符数组。在使用中，如果超出这个大小，就会重新分配内存，创建一个更大的数组，并将原先的数组复制过来，再丢弃旧的数组。在大多数情况下，你可以在创建StringBuffer的时候指定大小，这样就避免了在容量不够的时候自动增长，以提高性能。<br />
9,&nbsp;&nbsp; 使用Java NIO提高服务端程序的性能。<br />
10，考虑用静态工厂方法替代构造函数。<br />
11，在对大量的数组拷贝时，可以考虑用Arrays.copyOf()来拷贝。<br />
12, 在并发的情况下，要合理的选择同步的策略，应该谨慎的控制synchronized块的大小，不可以将一个操作分解到多个synchronized 但也要尽量地从synchronized块中分离耗时的且不影响并发的操作。<br />
13，要合理的选择集合框架，例如：ArrayList和LinkedList在某些不同的场合，其性能相差很大。对要做大量的插入时， LinkedList 的性能比ArrayList的性能好。对要做大量随机查找的时候用ArrayList的性能比用LinkedList的性能好。还有在不需要并发操作的情况下，选择非线程安全的集合比线程安全的集合要好。如在非线程安全的要求下，选择ArrayList要比Vector好。<br />
14，不要在循环语句块中调用length()方法做为循环结束的条件。<br />
15，如果字符串特别长，不要釆用charAt()一一的获取特定位置的字符，而应该调用toCharArray()方法转化为字符数组，然后通过数组 索引值获取指定位置的字符。<br />
16，如果是想把数据封装成Double类型的，不要这样使用new Double("1.23")，而要应这样使用new Double(1.23),虽然二者都没有语法 的错误，也都能达到预期的结果，但其性能有着很大的差异。<br />
17, 应尽量的通过缓冲流类来提高I/O操作效率,但也要合理的选择缓冲大小 。<br />
&nbsp;&nbsp;&nbsp; 呵呵，好了，今天就写到这吧，以后要是有时间再继续写。小弟我初学java，不对之地，欢迎大家指正，补遗。</font>
<img src ="http://www.blogjava.net/sutao/aggbug/162793.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sutao/" target="_blank">苏醄</a> 2007-11-24 10:24 <a href="http://www.blogjava.net/sutao/articles/162793.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>内存泄漏</title><link>http://www.blogjava.net/sutao/articles/162368.html</link><dc:creator>苏醄</dc:creator><author>苏醄</author><pubDate>Thu, 22 Nov 2007 06:13:00 GMT</pubDate><guid>http://www.blogjava.net/sutao/articles/162368.html</guid><wfw:comment>http://www.blogjava.net/sutao/comments/162368.html</wfw:comment><comments>http://www.blogjava.net/sutao/articles/162368.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sutao/comments/commentRss/162368.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sutao/services/trackbacks/162368.html</trackback:ping><description><![CDATA[<h1 style="color: rgb(0,0,0)"><span style="color: #0000ff">【转自IBM】Java的内存泄漏</span></h1>
<p style="color: rgb(0,0,0)"><font size="2"><a href="http://www-128.ibm.com/developerworks/cn/java/l-JavaMemoryLeak/#author"><span style="color: #0000ff">欧阳辰</span></a><span style="color: #0000ff">, <br />
</span><a href="http://www-128.ibm.com/developerworks/cn/java/l-JavaMemoryLeak/#author"><span style="color: #0000ff">周欣</span></a><span style="color: #0000ff">, </span></font></p>
<p style="color: rgb(0,0,0)"><span style="color: #0000ff">2002 年 10 月 21 日</span></p>
<blockquote style="color: rgb(0,0,0)"><span style="color: #0000ff">Java的一个重要优点就是通过垃圾收集器(Garbage Collection，GC)自动管理内存的回收，程序员不需要通过调用函数来释放内存。因此，很多程序员认为Java不存在内存泄漏问题，或者认为即使有内存泄漏也不是程序的责任，而是GC或JVM的问题。其实，这种想法是不正确的，因为Java也存在内存泄露，但它的表现与C++不同。</span></blockquote><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--end RESERVED FOR FUTURE USE INCLUDE FILES-->
<p style="color: rgb(0,0,0)"><span style="color: #0000ff"><a name="1"><span class="atitle">问题的提出</span></a></span></p>
<p style="color: rgb(0,0,0)"></p>
<p style="color: rgb(0,0,0)"><span style="color: #0000ff">Java 的一个重要优点就是通过垃圾收集器(Garbage Collection，GC)自动管理内存的回收，程序员不需要通过调用函数来释放内存。因此，很多程序员认为Java不存在内存泄漏问题，或者认为即使有内存泄漏也不是程序的责任，而是GC或JVM的问题。其实，这种想法是不正确的，因为Java也存在内存泄露，但它的表现与C++不同。</span></p>
<p style="color: rgb(0,0,0)"><span style="color: #0000ff">随着越来越多的服务器程序采用Java技术，例如JSP，Servlet， EJB等，服务器程序往往长期运行。另外，在很多嵌入式系统中，内存的总量非常有限。内存泄露问题也就变得十分关键，即使每次运行少量泄漏，长期运行之后，系统也是面临崩溃的危险。</span></p>
<font style="color: rgb(0,0,0)" size="2"><br />
</font>
<table style="color: rgb(0,0,0)" cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><span style="color: #0000ff"><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="550" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></span></td>
        </tr>
    </tbody>
</table>
<table class="no-print" style="color: rgb(0,0,0)" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><span style="color: #0000ff"><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="4" /><br />
            </span>
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><span style="color: #0000ff"><br />
                        </span></td>
                        <td valign="top" align="right"><span style="color: #0000ff"><br />
                        </span></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<font style="color: rgb(0,0,0)" size="2"><br />
<br />
</font>
<p style="color: rgb(0,0,0)"><span style="color: #0000ff"><a name="2"><span class="atitle">Java是如何管理内存</span></a></span></p>
<p style="color: rgb(0,0,0)"></p>
<p style="color: rgb(0,0,0)"><span style="color: #0000ff">为了判断Java中是否有内存泄露，我们首先必须了解Java是如何管理内存的。Java的内存管理就是对象的分配和释放问题。在Java中，程序员需要通过关键字new为每个对象申请内存空间 (基本类型除外)，所有的对象都在堆 (Heap)中分配空间。另外，对象的释放是由GC决定和执行的。在Java中，内存的分配是由程序完成的，而内存的释放是有GC完成的，这种收支两条线的方法确实简化了程序员的工作。但同时，它也加重了JVM的工作。这也是Java程序运行速度较慢的原因之一。因为，GC为了能够正确释放对象，GC必须监控每一个对象的运行状态，包括对象的申请、引用、被引用、赋值等，GC都需要进行监控。</span></p>
<p style="color: rgb(0,0,0)"><span style="color: #0000ff">监视对象状态是为了更加准确地、及时地释放对象，而释放对象的根本原则就是该对象不再被引用。</span></p>
<p style="color: rgb(0,0,0)"><span style="color: #0000ff">为了更好理解GC的工作原理，我们可以将对象考虑为有向图的顶点，将引用关系考虑为图的有向边，有向边从引用者指向被引对象。另外，每个线程对象可以作为一个图的起始顶点，例如大多程序从main进程开始执行，那么该图就是以main进程顶点开始的一棵根树。在这个有向图中，根顶点可达的对象都是有效对象， GC将不回收这些对象。如果某个对象 (连通子图)与这个根顶点不可达(注意，该图为有向图)，那么我们认为这个(这些)对象不再被引用，可以被GC回收。</span></p>
<p style="color: rgb(0,0,0)"><span style="color: #0000ff">以下，我们举一个例子说明如何用有向图表示内存管理。对于程序的每一个时刻，我们都有一个有向图表示JVM的内存分配情况。以下右图，就是左边程序运行到第6行的示意图。</span></p>
<font style="color: rgb(0,0,0)" size="2"><br />
<span style="color: #0000ff"><img height="201" alt="图1" src="http://www-128.ibm.com/developerworks/cn/java/l-JavaMemoryLeak/1.gif" width="582" /><br />
</span></font>
<p style="color: rgb(0,0,0)"><span style="color: #0000ff">Java 使用有向图的方式进行内存管理，可以消除引用循环的问题，例如有三个对象，相互引用，只要它们和根进程不可达的，那么GC也是可以回收它们的。这种方式的优点是管理内存的精度很高，但是效率较低。另外一种常用的内存管理技术是使用计数器，例如COM模型采用计数器方式管理构件，它与有向图相比，精度行低 (很难处理循环引用的问题)，但执行效率很高。</span></p>
<font style="color: rgb(0,0,0)" size="2"><br />
</font>
<table style="color: rgb(0,0,0)" cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><span style="color: #0000ff"><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="550" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></span></td>
        </tr>
    </tbody>
</table>
<table class="no-print" style="color: rgb(0,0,0)" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><span style="color: #0000ff"><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="4" /><br />
            </span>
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><span style="color: #0000ff"><br />
                        </span></td>
                        <td valign="top" align="right"><span style="color: #0000ff"><br />
                        </span></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<font style="color: rgb(0,0,0)" size="2"><br />
</font>
<p style="color: rgb(0,0,0)"><span style="color: #0000ff"><a name="3"><span class="atitle">什么是Java中的内存泄露</span></a></span></p>
<p style="color: rgb(0,0,0)"></p>
<p style="color: rgb(0,0,0)"><span style="color: #0000ff">下面，我们就可以描述什么是内存泄漏。在Java中，内存泄漏就是存在一些被分配的对象，这些对象有下面两个特点，首先，这些对象是可达的，即在有向图中，存在通路可以与其相连；其次，这些对象是无用的，即程序以后不会再使用这些对象。如果对象满足这两个条件，这些对象就可以判定为Java中的内存泄漏，这些对象不会被GC所回收，然而它却占用内存。</span></p>
<p style="color: rgb(0,0,0)"><span style="color: #0000ff">在C++中，内存泄漏的范围更大一些。有些对象被分配了内存空间，然后却不可达，由于C++中没有GC，这些内存将永远收不回来。在Java中，这些不可达的对象都由GC负责回收，因此程序员不需要考虑这部分的内存泄露。</span></p>
<p style="color: rgb(0,0,0)"><span style="color: #0000ff">通过分析，我们得知，对于C++，程序员需要自己管理边和顶点，而对于Java程序员只需要管理边就可以了(不需要管理顶点的释放)。通过这种方式，Java提高了编程的效率。</span></p>
<font style="color: rgb(0,0,0)" size="2"><br />
<span style="color: #0000ff"><img height="231" alt="图2" src="http://www-128.ibm.com/developerworks/cn/java/l-JavaMemoryLeak/2.gif" width="507" /> <br />
</span></font>
<p style="color: rgb(0,0,0)"><span style="color: #0000ff">因此，通过以上分析，我们知道在Java中也有内存泄漏，但范围比C++要小一些。因为Java从语言上保证，任何对象都是可达的，所有的不可达对象都由GC管理。</span></p>
<p style="color: rgb(0,0,0)"><span style="color: #0000ff">对于程序员来说，GC基本是透明的，不可见的。虽然，我们只有几个函数可以访问GC，例如运行GC的函数System.gc()，但是根据Java语言规范定义，该函数不保证JVM的垃圾收集器一定会执行。因为，不同的JVM实现者可能使用不同的算法管理GC。通常，GC的线程的优先级别较低。JVM调用GC的策略也有很多种，有的是内存使用到达一定程度时，GC才开始工作，也有定时执行的，有的是平缓执行GC，有的是中断式执行GC。但通常来说，我们不需要关心这些。除非在一些特定的场合，GC的执行影响应用程序的性能，例如对于基于Web的实时系统，如网络游戏等，用户不希望GC突然中断应用程序执行而进行垃圾回收，那么我们需要调整GC的参数，让GC能够通过平缓的方式释放内存，例如将垃圾回收分解为一系列的小步骤执行，Sun提供的HotSpot JVM就支持这一特性。</span></p>
<p style="color: rgb(0,0,0)"><span style="color: #0000ff">下面给出了一个简单的内存泄露的例子。在这个例子中，我们循环申请Object对象，并将所申请的对象放入一个Vector中，如果我们仅仅释放引用本身，那么Vector仍然引用该对象，所以这个对象对GC来说是不可回收的。因此，如果对象加入到Vector后，还必须从Vector中删除，最简单的方法就是将Vector对象设置为null。</span></p>
<table style="color: rgb(0,0,0)" cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td>
            <pre><font size="2"><code class="section"><br />
            <span style="color: #0000ff">Vector v=new Vector(10);<br />
            for (int i=1;i&lt;100; i++)<br />
            {<br />
            Object o=new Object();<br />
            v.add(o);<br />
            o=null;	<br />
            }</span></code></font></pre>
            </td>
        </tr>
    </tbody>
</table>
<font style="color: rgb(0,0,0)" size="2"><br />
<span style="color: #0000ff">//此时，所有的Object对象都没有被释放，因为变量v引用这些对象。 <br />
</span></font>
<table style="color: rgb(0,0,0)" cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><span style="color: #0000ff"><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="550" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></span></td>
        </tr>
    </tbody>
</table>
<table class="no-print" style="color: rgb(0,0,0)" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><span style="color: #0000ff"><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="4" /><br />
            </span>
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><span style="color: #0000ff"><br />
                        </span></td>
                        <td valign="top" align="right"><span style="color: #0000ff"><br />
                        </span></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<font style="color: rgb(0,0,0)" size="2"><br />
<br />
</font>
<p style="color: rgb(0,0,0)"><span style="color: #0000ff"><a name="4"><span class="atitle">如何检测内存泄漏</span></a></span></p>
<p style="color: rgb(0,0,0)"></p>
<p style="color: rgb(0,0,0)"><span style="color: #0000ff">最后一个重要的问题，就是如何检测Java的内存泄漏。目前，我们通常使用一些工具来检查Java程序的内存泄漏问题。市场上已有几种专业检查Java内存泄漏的工具，它们的基本工作原理大同小异，都是通过监测Java程序运行时，所有对象的申请、释放等动作，将内存管理的所有信息进行统计、分析、可视化。开发人员将根据这些信息判断程序是否有内存泄漏问题。这些工具包括Optimizeit Profiler，JProbe Profiler，JinSight , Rational 公司的Purify等。</span></p>
<p style="color: rgb(0,0,0)"><span style="color: #0000ff">下面，我们将简单介绍Optimizeit的基本功能和工作原理。</span></p>
<p style="color: rgb(0,0,0)"><span style="color: #0000ff">Optimizeit Profiler版本4.11支持Application，Applet，Servlet和Romote Application四类应用，并且可以支持大多数类型的JVM，包括SUN JDK系列，IBM的JDK系列，和Jbuilder的JVM等。并且，该软件是由Java编写，因此它支持多种操作系统。Optimizeit系列还包括Thread Debugger和Code Coverage两个工具，分别用于监测运行时的线程状态和代码覆盖面。</span></p>
<p style="color: rgb(0,0,0)"><span style="color: #0000ff">当设置好所有的参数了，我们就可以在OptimizeIt环境下运行被测程序，在程序运行过程中，Optimizeit可以监视内存的使用曲线(如下图)，包括JVM申请的堆(heap)的大小，和实际使用的内存大小。另外，在运行过程中，我们可以随时暂停程序的运行，甚至强行调用GC，让GC进行内存回收。通过内存使用曲线，我们可以整体了解程序使用内存的情况。这种监测对于长期运行的应用程序非常有必要，也很容易发现内存泄露。</span></p>
<font style="color: rgb(0,0,0)" size="2"><br />
<span style="color: #0000ff"><img height="354" alt="图3" src="http://www-128.ibm.com/developerworks/cn/java/l-JavaMemoryLeak/3.gif" width="521" /> <br />
</span></font>
<p style="color: rgb(0,0,0)"><span style="color: #0000ff">在运行过程中，我们还可以从不同视角观查内存的使用情况，Optimizeit提供了四种方式：</span></p>
<ul style="color: rgb(0,0,0)">
    <li><span style="color: #0000ff">堆视角。 这是一个全面的视角，我们可以了解堆中的所有的对象信息(数量和种类)，并进行统计、排序，过滤。了解相关对象的变化情况。</span></li>
    <li><span style="color: #0000ff">方法视角。通过方法视角，我们可以得知每一种类的对象，都分配在哪些方法中，以及它们的数量。</span></li>
    <li><span style="color: #0000ff">对象视角。给定一个对象，通过对象视角，我们可以显示它的所有出引用和入引用对象，我们可以了解这个对象的所有引用关系。</span></li>
    <li><span style="color: #0000ff">引用图。 给定一个根，通过引用图，我们可以显示从该顶点出发的所有出引用。</span></li>
</ul>
<p style="color: rgb(0,0,0)"><span style="color: #0000ff">在运行过程中，我们可以随时观察内存的使用情况，通过这种方式，我们可以很快找到那些长期不被释放，并且不再使用的对象。我们通过检查这些对象的生存周期，确认其是否为内存泄露。在实践当中，寻找内存泄露是一件非常麻烦的事情，它需要程序员对整个程序的代码比较清楚，并且需要丰富的调试经验，但是这个过程对于很多关键的Java程序都是十分重要的。</span></p>
<p style="color: rgb(0,0,0)"><span style="color: #0000ff">综上所述，Java也存在内存泄露问题，其原因主要是一些对象虽然不再被使用，但它们仍然被引用。为了解决这些问题，我们可以通过软件工具来检查内存泄露，检查的主要原理就是暴露出所有堆中的对象，让程序员寻找那些无用但仍被引用的对象。</span></p>
<img src ="http://www.blogjava.net/sutao/aggbug/162368.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sutao/" target="_blank">苏醄</a> 2007-11-22 14:13 <a href="http://www.blogjava.net/sutao/articles/162368.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Heap </title><link>http://www.blogjava.net/sutao/articles/162296.html</link><dc:creator>苏醄</dc:creator><author>苏醄</author><pubDate>Thu, 22 Nov 2007 02:30:00 GMT</pubDate><guid>http://www.blogjava.net/sutao/articles/162296.html</guid><wfw:comment>http://www.blogjava.net/sutao/comments/162296.html</wfw:comment><comments>http://www.blogjava.net/sutao/articles/162296.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sutao/comments/commentRss/162296.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sutao/services/trackbacks/162296.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;只有注册用户登录后才能阅读该文。<a href='http://www.blogjava.net/sutao/articles/162296.html'>阅读全文</a><img src ="http://www.blogjava.net/sutao/aggbug/162296.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sutao/" target="_blank">苏醄</a> 2007-11-22 10:30 <a href="http://www.blogjava.net/sutao/articles/162296.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA内存泄漏</title><link>http://www.blogjava.net/sutao/articles/161871.html</link><dc:creator>苏醄</dc:creator><author>苏醄</author><pubDate>Tue, 20 Nov 2007 07:30:00 GMT</pubDate><guid>http://www.blogjava.net/sutao/articles/161871.html</guid><wfw:comment>http://www.blogjava.net/sutao/comments/161871.html</wfw:comment><comments>http://www.blogjava.net/sutao/articles/161871.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sutao/comments/commentRss/161871.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sutao/services/trackbacks/161871.html</trackback:ping><description><![CDATA[<h1 id="pageName">JAVA内存泄漏，走开！</h1>
<div class="feature">&nbsp;-</div>
<div class="story">
<div id="ad_pcdog_big"><strong>摘要</strong><br />
　　尽管<a href="http://www.pcdog.com/special/1198/index.html" target="_blank">java</a>虚拟机和垃圾回收机制管理着大部分的内存事务，但是在<nobr oncontextmenu="return false;" onmousemove="kwM(0);" id="key0" onmouseover="kwE(event,0, this);" style="color: rgb(102,0,255); border-bottom: rgb(102,0,255) 1px dotted; background-color: transparent; text-decoration: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">java</nobr>软件中还是可能存在内存泄漏的情况。的确，在大型工程中，<nobr oncontextmenu="return false;" onmousemove="kwM(1);" id="key1" onmouseover="kwE(event,1, this);" style="color: rgb(102,0,255); border-bottom: rgb(102,0,255) 1px dotted; background-color: transparent; text-decoration: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">内存</nobr>泄漏是一个普遍问题。避免内存泄漏的第一步，就是要了解他们发生的原因。<strong>这篇文章就是要介绍一些常见的缺陷，然后提供一些非常好的实践例子来指导你写出没有内存泄漏的代码。</strong>一旦你的程序存在内存泄漏，要查明代码中引起泄漏的原因是很困难的。同时这篇文章也要介绍一个新的工具来查找内存泄漏，然后指明发生的根本原因。这个工具容易上手，可以让你找到产品级<a href="http://www.pcdog.com/net/3655/index.html" target="_blank">系统</a>中的内存泄漏。<br />
<br />
<strong>垃圾回收（GC）的角色</strong><br />
　　虽然垃圾回收关心着大部分的问题，包括内存管理，使得程序员的任务显得更加轻松，但是程序员还是可能犯些错误导致内存泄漏问题。GC(垃圾回收)通过递归对所有从&#8220;根&#8221;对象(堆栈中的对象，静态数据成员，JNI句柄等等)继承下来的引用进行<nobr oncontextmenu="return false;" onmousemove="kwM(2);" id="key2" onmouseover="kwE(event,2, this);" style="color: rgb(102,0,255); border-bottom: rgb(102,0,255) 1px dotted; background-color: transparent; text-decoration: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">工作</nobr>，然后标记所有可以访问的活动着的对象。而这些对象变成了程序唯一能够操纵的对象，其他的对象都被释放了。因为GC使得程序不能够访问那些被释放的对象，所以这样做是<a href="http://www.pcdog.com/special/2004/index.html" target="_blank">安全</a>的。<br />
<br />
　　内存管理可以说是自动的，但是这并没有让程序员脱离内存管理问题。比方说，对于内存的分配（还有释放）总是存在一定的开销，尽管这些开销对程序员来说是隐含的。一个程序如果创建了很多对象，那么它就要比完成相同任务而创建了较少对象的程序执行的速度慢（如果其他的条件都相同）。</div>
<p>　　文章更多想说的，导致内存泄漏主要的原因是，先前申请了内存空间而忘记了释放。如果程序中存在对无用对象的引用，那么这些对象就会驻留内存，消耗内存，因为无法让垃圾回收器验证这些对象是否不再需要。正如我们前面看到的，如果存在对象的引用，这个对象就被定义为&#8220;活动的&#8221;，同时不会被释放。要确定对象所占内存将被回收，程序员就要务必确认该对象不再会被使用。典型的做法就是把对象数据成员设为null或者从集合中移除该对象。注意，当局部变量不需要时，不需明显的设为null，因为一个方法执行完毕时，这些引用会自动被清理。<br />
<br />
　　从更高一个层次看，这就是所有存在内存管的<nobr oncontextmenu="return false;" onmousemove="kwM(3);" id="key3" onmouseover="kwE(event,3, this);" style="color: rgb(102,0,255); border-bottom: 0px dotted; background-color: transparent; text-decoration: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">语言</nobr>对内存泄漏所考虑的事情，剩余的对象引用将不再会被使用。<br />
<br />
<strong>典型的泄漏</strong><br />
　　既然我们知道了在java中确实会存在内存<nobr oncontextmenu="return false;" onmousemove="kwM(4);" id="key4" onmouseover="kwE(event,4, this);" style="color: rgb(102,0,255); border-bottom: 0px dotted; background-color: transparent; text-decoration: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">泄漏</nobr>，那么就让我们看一些典型的泄漏，并找出他们发生的原因。<br />
<br />
</p>
<p><strong>全局集合</strong><br />
　　在大型<nobr oncontextmenu="return false;" onmousemove="kwM(2);" id="key2" onmouseover="kwE(event,2, this);" style="color: rgb(102,0,255); border-bottom: rgb(102,0,255) 1px dotted; background-color: transparent; text-decoration: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">应用</nobr>程序中存在各种各样的全局<a href="http://www.pcdog.com/special/1152/index.html" target="_blank">数据仓库</a>是很普遍的，比如一个JNDI-tree或者一个session table。在这些情况下，注意力就被放在了管理数据仓库的大小上。当然是有一些适当的机制可以将仓库中的无用数据移除。<br />
<br />
　　可以有很多不同的<nobr oncontextmenu="return false;" onmousemove="kwM(3);" id="key3" onmouseover="kwE(event,3, this);" style="color: rgb(102,0,255); border-bottom: rgb(102,0,255) 1px dotted; background-color: transparent; text-decoration: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">解决</nobr>形式，其中最常用的是一种周期运行的清除作业。这个作业会验证仓库中的数据然后清除一切不需要的数据。</p>
<p>　　另一个办法是计算引用的数量。集合负责跟踪集合中每个元素的引用者数量。这要求引用者通知集合什么时候已经对元素处理完毕。当引用者的数目为零时，就可以移除集合中的相关元素。<br />
<br />
<strong>高速缓存</strong><br />
　　高速缓存是一种用来快速查找已经执行过的操作结果的<a href="http://www.pcdog.com/special/1002/index.html" target="_blank">数据结构</a>。因此，如果一个操作执行很慢的话，你可以先把普通输入的数据放入高速缓存，然后过些时间再调用高速缓存中的数据。<br />
高速缓存多少还有一点动态实现的意思，当数据操作完毕，又被送入高速缓存。一个典型的算法如下所示：<br />
　　１． 检查结果是否在高速缓存中，存在则返回结果；<br />
　　２． 如果结果不在，那么计算结果；<br />
　　３． 将结果放入高速缓存，以备将来的操作调用。</p>
<p>　　这个算法的问题（或者说潜在的<nobr oncontextmenu="return false;" onmousemove="kwM(1);" id="key1" onmouseover="kwE(event,1, this);" style="color: rgb(102,0,255); border-bottom: rgb(102,0,255) 1px dotted; background-color: transparent; text-decoration: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">内存</nobr>泄漏）在最后一步。如果操作是分别多次输入，那么存入高速缓存的内容将会非常大。很明显这个方法不可取。<br />
<br />
　　为了避免这种潜在的致命错误设计，程序就必须确定高速缓存在他所使用的内存中有一个上界。因此，更好的算法是：<br />
　　１． 检查结果是否在高速缓存中，存在则返回结果；<br />
　　２． 如果结果不在，那么计算结果；<br />
　　３． 如果高速缓存所占空间过大，移除缓存中旧的结果；<br />
　　４． 将结果放入高速缓存，以备将来的操作调用。<br />
<br />
　　通过不断的从缓存中移除旧的结果，我们可以假设，将来，最新输入的数据可能被重用的几率要远远大于旧的结果。这通常是一个不错的设想。</p>
<p>　　这个新的算法会确保高速缓存的容量在预先确定的范围内。精确的范围是很难计算的，因为缓存中的对象存在引用时将继续有效。正确的划分高速缓存的大小是一个复杂的任务，你必须权衡可使用内存大小和数据快速存取之间的矛盾。</p>
<p>　　另一个解决这个问题的途径是使用<nobr oncontextmenu="return false;" onmousemove="kwM(0);" id="key0" onmouseover="kwE(event,0, this);" style="color: rgb(102,0,255); border-bottom: rgb(102,0,255) 1px dotted; background-color: transparent; text-decoration: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">java</nobr>.lang.ref.SoftReference类来将对象放入高速缓存。这个方法可以保证当<a href="http://www.pcdog.com/special/1083/index.html" target="_blank">虚拟机</a>用完内存或者需要更多堆的时候，可以释放这些对象的引用。<br />
<br />
<strong>类装载器</strong><br />
　　Java类装载器创建就存在很多导致内存泄漏的<a href="http://www.pcdog.com/special/1216/index.html" target="_blank">漏洞</a>。由于类装载器的复杂结构，使得很难得到内存<nobr oncontextmenu="return false;" onmousemove="kwM(4);" id="key4" onmouseover="kwE(event,4, this);" style="color: rgb(102,0,255); border-bottom: rgb(102,0,255) 1px dotted; background-color: transparent; text-decoration: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">泄漏</nobr>的透视图。这些困难不仅仅是由于类装载器只与&#8220;普通的&#8221;对象引用有关，同时也和对象内部的引用有关，比如数据变量，方法和各种类。这意味着只要存在对数据变量，方法，各种类和对象的类装载器，那么类装载器将驻留在JVM中。既然类装载器可以同很多的类关联，同时也可以和静态数据变量关联，那么相当多的内存就可能发生泄漏。<br />
<br />
<br />
<strong>定位<nobr oncontextmenu="return false;" onmousemove="kwM(1);" id="key0" onmouseover="kwE(event,1, this);" style="color: rgb(102,0,255); border-bottom: 2px dotted; background-color: transparent; text-decoration: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">内存</nobr>泄漏</strong><br />
　　常常地，程序内存泄漏的最初迹象发生在出错之后，在你的程序中得到一个OutOfMemoryError。这种典型的情况发生在产品环境中，而在那里，你希望内存泄漏尽可能的少，调试的可能性也达到最小。 </p>
<div class="ad0">也许你的测试环境和产品的<a href="http://www.pcdog.com/net/3655/index.html" target="_blank">系统</a>环境不尽相同，导致泄露的只会在产品中暴露。这种情况下，你需要一个低负荷的工具来监听和寻找内存泄漏。同时，你还需要把这个工具同你的<nobr oncontextmenu="return false;" onmousemove="kwM(2);" id="key1" onmouseover="kwE(event,2, this);" style="color: rgb(102,0,255); border-bottom: rgb(102,0,255) 1px dotted; background-color: transparent; text-decoration: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">系统</nobr>联系起来，而不需要重新<a href="http://www.pcdog.com/net/3604/index.html" target="_blank">启动</a>他或者机械化你的代码。也许更重要的是，当你做分析的时候，你需要能够同工具分离而使得系统不会受到干扰。<br />
<br />
　　一个OutOfMemoryError常常是内存泄漏的一个标志，有可能应用程序的确用了太多的内存；这个时候，你既不能增加JVM的堆的数量，也不能改变你的程序而使得他减少内存使用。但是，在大多数情况下，一个OutOfMemoryError是内存泄漏的标志。一个<nobr oncontextmenu="return false;" onmousemove="kwM(3);" id="key2" onmouseover="kwE(event,3, this);" style="color: rgb(102,0,255); border-bottom: rgb(102,0,255) 1px dotted; background-color: transparent; text-decoration: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">解决</nobr>办法就是继续监听GC的活动，看看随时间的流逝，内存使用量是否会增加，如果有，程序中一定存在内存泄漏。<br />
<br />
<strong>详细输出<br />
</strong>　　有很多办法来监听垃圾回收器的活动。也许运用最广泛的就是以：-Xverbose:gc选项运行JVM，然后观察输出结果一段时间。<br />
<br />
　　[memory] 10.109-10.235: GC 65536K-&gt;16788K (65536K), 126.000 ms<br />
<br />
　　箭头后的值（在这个例子中 16788K）是垃圾回收后堆的使用量。<br />
<br />
<strong>控制台</strong><br />
　　观察这些无尽的GC详细统计输出是一件非常单调乏味的事情。好在有一些工具来代替我们做这些事情。The JRockit Management Console可以用图形的方式输出堆的使用量。通过观察图像，我们可以很方便的观察堆的使用量是否伴随时间增长。<br />
</div>
<p align="center"><a href="http://www.pcdog.com/ArtImage/20051225/59619-1.jpg" target="_blank"><img alt="JAVA内存泄漏，走开！" src="http://www.pcdog.com/ArtImage/20051225/59619-1.jpg" border="0" /></a></p>
<p align="center">Figure 1. The JRockit Management Console</p>
<p>　　管理控制台甚至可以配置成在堆使用量出现问题（或者其他的事件发生）时向你发送邮件。这个显然使得监控内存泄漏更加容易。<br />
<br />
<strong>内存泄漏探测工具</strong><br />
　　有很多专门的内存<nobr oncontextmenu="return false;" onmousemove="kwM(4);" id="key3" onmouseover="kwE(event,4, this);" style="color: rgb(102,0,255); border-bottom: rgb(102,0,255) 1px dotted; background-color: transparent; text-decoration: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">泄漏</nobr>探测工具。其中The JRockit Memory Leak Detector可以供来观察内存泄漏也可以针对性地找到泄漏的原因。这个强大的工具被紧密地集成在JRockit JVM中，可以提供最低可能的内存事务也可以轻松的访问<a href="http://www.pcdog.com/special/1083/index.html" target="_blank">虚拟机</a>的堆。<br />
<br />
<br />
<strong>专门工具的优势</strong><br />
　　一旦你知道程序中存在<nobr oncontextmenu="return false;" onmousemove="kwM(3);" id="key3" onmouseover="kwE(event,3, this);" style="color: rgb(102,0,255); border-bottom: rgb(102,0,255) 1px dotted; background-color: transparent; text-decoration: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">内存</nobr>泄漏，你需要更专业的工具来查明为什么这里会有泄漏。而JVM是不可能告诉你的。现在有很多工具可以利用了。这些工具本质上主要通过两种方法来得到JVM的存储<a href="http://www.pcdog.com/net/3655/index.html" target="_blank">系统</a><nobr oncontextmenu="return false;" onmousemove="kwM(0);" id="key0" onmouseover="kwE(event,0, this);" style="color: rgb(102,0,255); border-bottom: rgb(102,0,255) 1px dotted; background-color: transparent; text-decoration: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">信息</nobr>的：JVMTI和字节码仪器。 <script type="text/javascript"><!-- google_ad_client="pub-1572879403720716" ; google_ad_width="336;
google_ad_height" = 280; google_ad_format="336x280_as" ; google_ad_type="text_image" ; google_ad_channel="2957605308" ; google_alternate_ad_url="http://www.pcdog.com/js/336.htm" ; google_color_border="F5FAFA" ; google_color_bg="F5FAFA" ; google_color_link="1F3A87" ; google_color_url="0000FF" ; google_color_text="000000" google_language='zh-CN' ;
//--></script><script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
</script></p>
<p>Java虚拟机工具接口（JVMTI）和他的原有形式JVMPI（压型接口，profiling Interface）都是标准接口，作为外部工具同JVM进行通信，搜集JVM的信息。字节码仪器则是引用通过探针获得工具所需的字节信息的预处理技术。<br />
<br />
　　通过这些技术来侦测内存泄漏存在两个缺点，而这使得他们在产品级环境中的运用不够理想。首先，根据两者对内存的使用量和内存事务性能的降级是不可以忽略的。从JVM获得的堆的使用量信息需要在工具中导出，收集和处理。这意味着要分配内存。按照JVM的性能导出信息是需要开销的，垃圾回收器在搜集信息的时候是运行的非常缓慢的。另一个缺点就是，这些工具所需要的信息是关系到JVM的。让工具在JVM开始运行的时候和它关联，而在分析的时候，分离工具而保持 JVM运行，这显然是不可能的。 </p>
<p>　　既然JRockit Memory Leak Detector是被集成到JVM中的，那么以上两种缺点就不再存在。首先，大部分的处理和分析都是在JVM中完成的，所以就不再需要传送或重建任何数据。处理也可以建立在垃圾回收器的<nobr oncontextmenu="return false;" onmousemove="kwM(5);" id="key4" onmouseover="kwE(event,5, this);" style="color: rgb(102,0,255); border-bottom: rgb(102,0,255) 1px dotted; background-color: transparent; text-decoration: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">基础</nobr>上，即提高速度。再有，内存泄漏侦测器可以同一个运行的JVM关联和分离，只要JVM在开始的时候伴随着 &#8211;Xmanagement选项（通过<nobr oncontextmenu="return false;" onmousemove="kwM(1);" id="key1" onmouseover="kwE(event,1, this);" style="color: rgb(102,0,255); border-bottom: rgb(102,0,255) 1px dotted; background-color: transparent; text-decoration: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">远程</nobr>JMX接口允许监听和管理JVM）。当工具分离以后，工具不会遗留任何东西在JVM中；JVM就可以全速运行代码就好像工具关联之前一样。<br />
<br />
<strong>趋势分析</strong><br />
　　让我们更深一步来观察这个工具，了解他如何捕捉到内存泄漏。在你了解到代码中存在内存泄漏，第一步就是尝试计算出什么数据在泄漏——哪个对象类导致泄露。The JRockit Memory Leak Detector通过在垃圾回收的时候，计算每个类所包含的现有的对象来达到目的。如果某一个类的对象成员数目随着时间增长（增长率），那么这里很可能存在泄漏。<br />
</p>
<p align="center"><a href="http://www.pcdog.com/ArtImage/20051225/59619-2.gif" target="_blank"><img alt="JAVA内存泄漏，走开！" src="http://www.pcdog.com/ArtImage/20051225/59619-2.gif" border="0" /></a></p>
<p align="center">Figure 2. The trend analysis view of the Memory Leak Detector</p>
<p>　　因为一个<nobr oncontextmenu="return false;" onmousemove="kwM(6);" id="key5" onmouseover="kwE(event,6, this);" style="color: rgb(102,0,255); border-bottom: rgb(102,0,255) 1px dotted; background-color: transparent; text-decoration: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">泄漏</nobr>很可能只是像水滴一样小，所以趋势分析必须运行足够长的一段时间。在每个短暂的时间段里，局部类的增加会使得泄漏发生推迟。但是，内存事务是非常小的（最大的内存事务是由在每个垃圾回收时从JRockit向内存泄漏探测器发送的一个<a href="http://www.pcdog.com/net/3618/index.html" target="_blank">数据包</a>组成的）。内存事务不应该成为任何系统的问题——甚至一个在产品阶段全速运行的程序。<br />
一开始，<nobr oncontextmenu="return false;" onmousemove="kwM(2);" id="key2" onmouseover="kwE(event,2, this);" style="color: rgb(102,0,255); border-bottom: rgb(102,0,255) 1px dotted; background-color: transparent; text-decoration: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">数字</nobr>会有很大的跳转，随时间的推进，这些数字会变得稳定，而后显示哪些类会不断的增大。<br />
</p>
<p><br />
</p>
<p><strong>寻找根本原因</strong><br />
　　知道那些对象的类会导致泄露，有时候足够制止泄露问题。这个类也许只是被用在非常有限的部分，通过快速的视察就可以找到问题所在。不幸的是，这些信息是不够的。 <script type="text/javascript"><!-- google_ad_client="pub-1572879403720716" ; google_ad_width="336;
google_ad_height" = 280; google_ad_format="336x280_as" ; google_ad_type="text_image" ; google_ad_channel="2957605308" ; google_alternate_ad_url="http://www.pcdog.com/js/336.htm" ; google_color_border="F5FAFA" ; google_color_bg="F5FAFA" ; google_color_link="1F3A87" ; google_color_url="0000FF" ; google_color_text="000000" google_language='zh-CN' ;
//--></script><script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
</script></p>
<p>比方说，经常导致内存泄漏的对象类<nobr oncontextmenu="return false;" onmousemove="kwM(0);" id="key0" onmouseover="kwE(event,0, this);" style="color: rgb(102,0,255); border-bottom: rgb(102,0,255) 1px dotted; background-color: transparent; text-decoration: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">java</nobr>.lang.String，然而String类被应用于整个程序，这就变得有些无助。<br />
<br />
　　我们想知道的是其他的对象是否会导致<nobr oncontextmenu="return false;" onmousemove="kwM(2);" id="key1" onmouseover="kwE(event,2, this);" style="color: rgb(102,0,255); border-bottom: rgb(102,0,255) 1px dotted; background-color: transparent; text-decoration: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">内存</nobr>泄漏，好比上面提到的String类，为什么这些导致泄漏的对象还是在周围存在？哪些引用是指向这些对象的？如果列出所有引用String的对象，工作就会变得太大而没有实际意义。为了限制数据的数量，我们可以通过类把他们编成一个组，这样我们就可以看到，那些其他类的对象会依然泄漏对象（String 类）。比如，将一个String类放入Hashtable，那里我们可以看到关联到String类的Hashtable入口。从Hashtable入口向后运行，我们终于找到那些关联到String类的Hashtable对象（参看图三如下）。<br />
</p>
<p align="center"><a href="http://www.pcdog.com/ArtImage/20051225/59619-3.gif" target="_blank"><img alt="JAVA内存泄漏，走开！" src="http://www.pcdog.com/ArtImage/20051225/59619-3.gif" border="0" /></a></p>
<p align="center">Figure 3. Sample view of the type graph as seen in the tool</p>
<p><strong>向后工作</strong><br />
　　自从开始我们就一直着眼于对象类，而不是单独的对象，我们不知道那个Hashtable存在泄漏。如果我们可以找出所有的Hashtable在<a href="http://www.pcdog.com/net/3655/index.html" target="_blank">系统</a>中有多大，我们可以假设最大的那个Hashtable存在泄漏（因为它可以聚集足够的泄漏而变得很大）。因此，所有Hashtable，同时有和所有他们所涉及的数据，可以帮助我们查明导致泄露的精确的Hashtable。<br />
</p>
<p align="center"><a href="http://www.pcdog.com/ArtImage/20051225/59619-4.gif" target="_blank"><img alt="JAVA内存泄漏，走开！" src="http://www.pcdog.com/ArtImage/20051225/59619-4.gif" border="0" /></a></p>
<p align="center">Figure 4. Screenshot of the list of Hashtable objects and the size of the data they are holding live</p>
<p>　　计算一个对象所涉及的数据的开销是非常大的（这要求引用<nobr oncontextmenu="return false;" onmousemove="kwM(5);" id="key2" onmouseover="kwE(event,5, this);" style="color: rgb(102,0,255); border-bottom: rgb(102,0,255) 1px dotted; background-color: transparent; text-decoration: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">图表</nobr>伴随着那个对象作为根运行）而且如果对每一个对象都这样处理，就需要很多时间。知道一些关于Hashtable内部的实现机制可以带来捷径。在内部，一个 Hashtable有一个Hashtable的数组入口。数组的增长伴随着Hashtable中对象的增长。因此，要找到最大的Hashtable，我们可以把搜索限制在寻找包含Hashtable引用入口的最大的数组。这样就更快捷了。<br />
</p>
<p align="center"><a href="http://www.pcdog.com/ArtImage/20051225/59619-5.gif" target="_blank"><img alt="JAVA内存泄漏，走开！" src="http://www.pcdog.com/ArtImage/20051225/59619-5.gif" border="0" /></a></p>
<p align="center">Figure 5. Screenshot of the listing of the largest Hashtable entry arrays, as well as their sizes.</p>
<p><br clear="all" />
</p>
<br />
<p><strong>向下深入</strong><br />
　　当我们发现了存在泄漏的Hashtable的实例，就可以顺藤摸瓜找到其他的引用这些Hashtable的实例，然后用上面的方法来找到是那个Hashtable存在问题。<br />
</p>
<p align="center"><a href="http://www.pcdog.com/ArtImage/20051225/59619-6.gif" target="_blank"><img alt="JAVA内存泄漏，走开！" src="http://www.pcdog.com/ArtImage/20051225/59619-6.gif" border="0" /></a><script type="text/javascript"><!-- google_ad_client="pub-1572879403720716" ; google_ad_width="336;
google_ad_height" = 280; google_ad_format="336x280_as" ; google_ad_type="text_image" ; google_ad_channel="2957605308" ; google_alternate_ad_url="http://www.pcdog.com/js/336.htm" ; google_color_border="F5FAFA" ; google_color_bg="F5FAFA" ; google_color_link="1F3A87" ; google_color_url="0000FF" ; google_color_text="000000" google_language='zh-CN' ;
//--></script><script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
</script></p>
<p align="center">Figure 6. This is what an instance graph can look like in the tool.</p>
<p>　　举个例子，一个Hashtable可以有一个来自MyServer的对象的引用，而MyServer包含一个activeSessions数据成员。这些信息就足够深入代码找出问题所在。<br />
</p>
<p align="center"><a href="http://www.pcdog.com/ArtImage/20051225/59619-7.gif" target="_blank"><img alt="JAVA内存泄漏，走开！" src="http://www.pcdog.com/ArtImage/20051225/59619-7.gif" border="0" /></a></p>
<p align="center">Figure 7. Inspecting an object and its references to other objects</p>
<p><strong>找出分配点</strong><br />
　　当发现了内存泄漏问题，找到那些泄漏的对象在何处是非常有用的。也许没有足够的信息知道他们同其他相关对象之间的联系，但是关于他们在那里被创建的信息还是很有帮助的。当然，你不会愿意创建一个工具来打印出所有分配的堆栈路径。你也不会愿意在模拟环境中运行程序只是为了捕捉到一个内存泄漏。</p>
<p>　　有了JRockit Memory Leak Detector，程序代码可以动态的在内存分配出创建堆栈路径。这些堆栈路径可以在工具中累积，分析。如果你不启用这个工具，这个特征就不会有任何消耗，这就意味着时刻准备着开始。当需要分配路径时，JRockit的编译器可以让代码不工作，而监视内存分配，但只对需要的特定类有效。更好的是，当做完数据分析后，生成的机械代码会完全被移除，不会引起任何执行上的效率衰退。<br />
</p>
<p align="center"><a href="http://www.pcdog.com/ArtImage/20051225/59619-8.gif" target="_blank"><img alt="JAVA内存泄漏，走开！" src="http://www.pcdog.com/ArtImage/20051225/59619-8.gif" border="0" /></a></p>
<p align="center">Figure 8. The allocation stack traces for String during execution of a sample program</p>
<p><strong>总结</strong><br />
　　内存泄漏查找起来非常困难，文章中的一些避免泄漏的好的实践，包括了要时刻记住把什么放进了<a href="http://www.pcdog.com/special/1002/index.html" target="_blank">数据结构</a>中，更接近的监视内存中意外的增长。<br />
<br />
　　我们同时也看到了JRockit Memory Leak Detector是如何捕捉产品级<a href="http://www.pcdog.com/net/3655/index.html" target="_blank">系统</a>中的内存泄漏的。该工具通过三步的方法发现泄漏。一，通过趋势分析发现那些对象类存在泄漏；二，找出同泄漏对象相关的其他类；三，向下发掘，观察独立的对象之间是如何相互联系的。同时，该工具也可以动态的，找出所有内存分配的堆栈路径。利用这三个特性，将该工具紧紧地集成在JVM中，那么就可以<a href="http://www.pcdog.com/special/2004/index.html" target="_blank">安全</a>的，有效的捕捉和修复内存泄漏了。<br />
<br />
<strong>资源</strong><br />
　　JRockit Tools Download <br />
　　BEA JRockit 5.0 Documentation <br />
　　New Features and Tools in JRockit 5.0 <br />
　　BEA JRockit DevCenter <br />
<br />
Staffan Larsen是JRockit项目的工程师之一，这个项目是在1998年底他与别人联合创建的。</p>
</div>
<img src ="http://www.blogjava.net/sutao/aggbug/161871.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sutao/" target="_blank">苏醄</a> 2007-11-20 15:30 <a href="http://www.blogjava.net/sutao/articles/161871.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java编程中异常处理的优劣之道</title><link>http://www.blogjava.net/sutao/articles/161830.html</link><dc:creator>苏醄</dc:creator><author>苏醄</author><pubDate>Tue, 20 Nov 2007 04:02:00 GMT</pubDate><guid>http://www.blogjava.net/sutao/articles/161830.html</guid><wfw:comment>http://www.blogjava.net/sutao/comments/161830.html</wfw:comment><comments>http://www.blogjava.net/sutao/articles/161830.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sutao/comments/commentRss/161830.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sutao/services/trackbacks/161830.html</trackback:ping><description><![CDATA[<h1 id="pageName"><span style="color: #ff0000">Java编程中异常处理的优劣之道</span></h1>
<div class="story">
<p><span style="color: #0000ff">Java编程中的异常处理是一个很常见的话题了，几乎任何一门介绍性的Java课程都会提到异常处理。不过，我认为很多人其实并没有真正掌握正确处理异常情况的方法和策略，最多也就不过了解个大概，知道点概念。<br />
<br />
<br />
本文就对三种不同程度和质量的Java异常处理进行了讨论，所阐述的处理异常的方式按手法的高下分为： </span></p>
<p><span style="color: #0000ff">好，不好和恶劣三种。</span></p>
<p><span style="color: #0000ff">同时向你提供了一些解决这些问题的技巧。 </span></p>
<p><span style="color: #0000ff">首先解释一些Java异常处理中必须搞清楚的定义和机制。Java语言规范将自Error类或RuntimeException类衍生出来的任何违例都称作&#8220;不可检查&#8221;(Unchecked)异常；其他所有异常则称作&#8220;可检查&#8221;(Checked)异常。 </span></p>
<p><span style="color: #0000ff">所谓可检查异常，是指我们应该自行处理的异常。至于处理的手段，要么加以控制(try catch)，要么通告(throws)他们有可能产生。通常，应捕捉那些已知如何处理的异常，而通告那些不知如何处理的异常。 </span></p>
<p><span style="color: #0000ff">而对那些不可检查异常来说，他们要么在我们的控制之外(Error)，要么是我们首先就不该允许的情况(RuntimeException)。 </span></p>
<p><span style="color: #0000ff">至于异常的指定，Java的规则非常简单：一个方法必须通告自己可能产生的所有可检查异常。编写自己的方法时，并不一定要通告出方法实际可能产生的每一个异常对象，要想理解什么时候必须要方法的throws丛句来通告异常，就必须知道对一个异常来说，他只有可能在下面四种情况下才会产生： </span></p>
<p><span style="color: #0000ff">1、调用了可能产生异常的方法。比如BufferedReader类的readLine方法。该方法通告java.io.IOException异常。</span></p>
<p><span style="color: #0000ff">2、侦测到一个错误，并用throw语句产生异常。 </span></p>
<p><span style="color: #0000ff">3、出现一个编程错误。比如a[-1] = 0。 </span></p>
<p><span style="color: #0000ff">4、Java产生内部错误。 </span></p>
<p><span style="color: #0000ff">如果出现头两种情况之一，必须告诉打算使用自己方法的人：假如使用这个方法，可能造成一个异常的产生(即在方法头上使用throws)，一个简单的记忆方法： </span></p>
<p><span style="color: #0000ff">只要含有throw，就要通告throws。如果一个方法必须同时处理多个异常，就必须在头内指出所有异常。就像下例展示的那样，用逗号对他们进行分割： </span></p>
<p></p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre><span style="color: #0000ff">1234567 class Animation&nbsp; <br />
            <br />
            { <br />
            <br />
            public Image loadImage(Strint s)&nbsp; throws EOFException,MalformedURLException<br />
            <br />
            { <br />
            <br />
            　&#8230;&#8230;&#8230;&#8230; <br />
            <br />
            } <br />
            <br />
            }</span></pre>
            </td>
        </tr>
    </tbody>
</table>
<p><span style="color: #0000ff">然而，我们不需要通告内部java错误，也不应该通告自RuntimeException衍生出来的异常。 </span></p>
<p><span style="color: #0000ff"><strong>好的异常处理</strong> </span></p>
<p><span style="color: #0000ff">好异常处理提供了处理程序错误的统一机制。事实上，Java语言通过向调用者提出异常警告的方式而显着地提升了软件开发中的异常处理能力。这种方式把Java语言中的&#8220;方法（method）&#8221;进行了扩展和增强，使之包括了自身的错误条件。下面就让我们看一个例子，这个例子说明了这种情况。 </span></p>
<p><span style="color: #0000ff">以下是FileInputStream构造器之一的原型： </span></p>
<p></p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre><span style="color: #0000ff">public FileInputStream(String name) throws FileNotFoundException Java</span></pre>
            </td>
        </tr>
    </tbody>
</table>
<p><span style="color: #0000ff">的方法和构造器必须声明他们在被调用时可能&#8220;扔出&#8221;的异常，采用的关键字就是&#8220;throws&#8221;。这种在方法原型中出现的异常提示增加了编程的可靠性。 </span></p>
<p><span style="color: #0000ff">显而易见，这种方式是向方法的调用者提示了可能出现的异常条件，这样调用者就可以对这些异常作出适当的相应处理。以下代码示意我们是如何捕获并且处理FileNotFoundException 这一异常的： </span></p>
<p></p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre><span style="color: #0000ff">1234567891011 try&nbsp; <br />
            <br />
            {&nbsp;&nbsp; <br />
            <br />
            FileInputStream fis = new FileInputStream(args[0]);&nbsp;&nbsp;&nbsp;&nbsp; <br />
            <br />
            // other code here ...&nbsp;&nbsp; <br />
            <br />
            }&nbsp; <br />
            <br />
            catch (FileNotFoundException fnfe)&nbsp; <br />
            <br />
            {&nbsp; <br />
            <br />
            System.out.println("File: " + args[0] + " not found. Aborting.");&nbsp; <br />
            <br />
            System.exit(1);&nbsp; <br />
            <br />
            }</span></pre>
            </td>
        </tr>
    </tbody>
</table>
<p><span style="color: #0000ff">Java异常处理还有其他一些优秀的特性，这就是可检查异常、用户定义异常和在JDK 1.4中推出的新型Java记录API（Java Logging API）。java.lang.Exception的所有子类都属于可检查异常。可检查异常（checked exception）是扔出该异常的方法所必须提示的异常，这种异常必须被捕获或者向调用者提示。用户定义异常（User-defined exceptions）是定制的异常类，这种异常类扩展了java.lang.Exception类。优良的Java程序规定定制异常封装、报告和处理他们自己独有的情况。最新的Java记录API（logging API）则可以集中记录异常。</span></p>
<p><span style="color: #0000ff"><strong>不好的Java异常处理</strong> </span></p>
<p><span style="color: #0000ff">不好的一面包括两种情况：滥用不可检查异常（unchecked exceptions）和滥用catchall构造器等。这两种方式都使得问题变得复杂起来。 </span></p>
<p><span style="color: #0000ff">有一种类别的异常属于RuntimeException的子类，这种异常不会受到编译器的检查。比如，NullPointerException和 ArrayStoreException就是这种类型异常的实例。程序员可以对RuntimeException进行子类化以回避检查异常的限制，从而便于产生这些异常的方法为其调用者所使用。 </span></p>
<p><span style="color: #0000ff">专业的开发团队应当只允许在很少的情况下才可以这样做。 </span></p>
<p><span style="color: #0000ff">第二种异常处理的陋习是catchall构造器。所谓的&#8220;catchall 构造器&#8221;就是一种异常捕获代码模块，它可以处理所有扔给它的可能异常。 </span></p>
<p><span style="color: #0000ff">以下是catchall处理器的实例： </span></p>
<p></p>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre><span style="color: #0000ff">123456789&nbsp; try <br />
            <br />
            {&nbsp; <br />
            <br />
            // code here with checked exceptions <br />
            <br />
            } <br />
            <br />
            catch (Throwable t)&nbsp; <br />
            <br />
            {&nbsp; <br />
            <br />
            t.printStackTrace();&nbsp; <br />
            <br />
            }</span></pre>
            </td>
        </tr>
    </tbody>
</table>
<p><span style="color: #0000ff">我得承认，我自己在编写一般程序的时候就曾经用过这种技术；但是，在编写关键程序的时候这种类型的构造器一定要避免使用，除非他们被授权可以和中央错误处理器联合使用才可以这样做。 </span></p>
<p><span style="color: #0000ff">除此之外，catchall构造器不过只是一种通过避免错误处理而加快编程进度的机制。 </span></p>
<p><span style="color: #0000ff">异常处理的一个不足之处是难以采用优良的错误处理策略。从低容内存状态恢复、写入错误和算法错误等异常情况都不是轻易能得到解决的。你可以尝试一下循环、垃圾收集和提醒用户等常用技术来应付以上的局面。 </span></p>
<p><span style="color: #0000ff"><strong>恶劣的处理方法</strong> </span></p>
<p><span style="color: #0000ff">和许多Java特性及其API类似，Java的异常处理机制也有&#8220;霸王硬上弓&#8221;类的滑稽错误。比方说，为了扔出某个异常竟然毫不犹豫地用&#8220;new&#8221;关键词为其分配内存就是这样的例子。 </span></p>
<p><span style="color: #0000ff">我自己不知道有多少次就因为犯了这种错误而在严肃的编译器面前屡屡碰壁。在这种情况下，我们其实都是在伺候语言而不是让语言为我们所用。还有我们碰到的OutOfMemoryErrors就是异常处理的缺陷。这一处理过程是： </span></p>
<p><span style="color: #0000ff">使用finally模块关闭文件，解析异常以得到出现问题的方法和代码行。在这一过程之内最大的缺陷是需要捕获OutOfMemoryError，而这一异常却并不是可检查异常！想想看，内存耗尽是相当常见的情况。任何与内存使用状态紧密相关的程序都应当捕获和处理这一错误。 </span></p>
<p><span style="color: #0000ff"><strong>使用异常时的一些建议</strong> </span></p>
<p><span style="color: #0000ff">1、异常控制的设计宗旨并不是用来代替一些简单的测试。只有在异常情况下才使用异常！ </span></p>
<p><span style="color: #0000ff">2、不要过分细化异常。不要在每个语句上都加上异常处理，最好将整个任务都放在try块内。如果其中有一项操作失败，可以随即放弃任务。 </span></p>
<p><span style="color: #0000ff">3、不要&#8220;压制&#8221;异常。对于需要通告异常的方法，我们可以改用捕捉的方法来将异常强行关闭，如果真的出现异常，那个异常会被&#8220;静悄悄&#8221;的忽略。如果觉得产生的异常会非常重要，就必须多费些功夫，对其进行正确的控制。 </span></p>
<p><span style="color: #0000ff">4、不要介意异常的传递。如果调用的方法会产生异常，比如readLine方法，他们天生就能捕捉自己可能产生的异常，在这种情况下，一种更好地做法是将这些异常传递出去，而不是自己动手来捕捉它。</span></p>
</div>
<img src ="http://www.blogjava.net/sutao/aggbug/161830.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sutao/" target="_blank">苏醄</a> 2007-11-20 12:02 <a href="http://www.blogjava.net/sutao/articles/161830.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java异常处理</title><link>http://www.blogjava.net/sutao/articles/161683.html</link><dc:creator>苏醄</dc:creator><author>苏醄</author><pubDate>Mon, 19 Nov 2007 09:51:00 GMT</pubDate><guid>http://www.blogjava.net/sutao/articles/161683.html</guid><wfw:comment>http://www.blogjava.net/sutao/comments/161683.html</wfw:comment><comments>http://www.blogjava.net/sutao/articles/161683.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sutao/comments/commentRss/161683.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sutao/services/trackbacks/161683.html</trackback:ping><description><![CDATA[<span style="color: #000080"><span style="color: #0000ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>
<p><span style="color: #000080"><span style="color: #0000ff">Java 语言要求 java 程序中（无论是谁写的代码），所有抛出（ throw ）的异常都必须是从 Throwable 派生而来。 当然，实际的 Java 编程中，由于 JDK 平台已经为我们设计好了非常丰富和完整的异常对象分类模型。因此， java 程序员一般是不需要再重新定义自己的异常对象。而且即便是需要扩展自定义的异常对象，也往往会从 Exception 派生而来。所以，对于 java 程序员而言，它一般只需要在它的顶级函数中 catch(Exception ex) 就可以捕获出所有的异常对象。 所有异常对象的根基类是 Throwable ， Throwable 从 Object 直接继承而来（这是 java 系统所强制要求的），并且它实现了 Serializable 接口（这为所有的异常对象都能够轻松跨越 Java 组件系统做好了最充分的物质准备）。从 Throwable 直接派生出的异常类有 Exception 和 Error 。 Exception 是 java 程序员所最熟悉的，它一般代表了真正实际意义上的异常对象的根基类。也即是说， Exception 和从它派生而来的所有异常都是应用程序能够 catch 到的，并且可以进行异常错误恢复处理的异常类型。而 Error 则表示 Java 系统中出现了一个非常严重的异常错误，并且这个错误可能是应用程序所不能恢复的，例如 LinkageError ，或 ThreadDeath 等。 <br />
首先还是看一个例子吧！代码如下： <br />
import java.io.*; <br />
public class Trans <br />
{ <br />
public static void main(String[] args) <br />
{ <br />
try <br />
{ <br />
BufferedReader rd=null; <br />
Writer wr=null; <br />
try <br />
{ <br />
File srcFile = new File((args[0])); <br />
File dstFile = new File((args[1])); <br />
rd = new BufferedReader(new InputStreamReader(new FileInputStream(srcFile), args[2])); <br />
wr = new OutputStreamWriter(new FileOutputStream(dstFile), args[3]); <br />
// 注意下面这条语句，它有什么问题吗？ <br />
if (rd == null || wr == null) throw new Exception("error! test!"); <br />
while(true) <br />
{ <br />
String sLine = rd.readLine(); <br />
if(sLine == null) break; <br />
wr.write(sLine); <br />
wr.write("\r\n"); <br />
} <br />
} <br />
finally <br />
{ <br />
wr.flush(); <br />
wr.close(); <br />
rd.close(); <br />
} <br />
} <br />
catch(IOException ex) <br />
{ <br />
ex.printStackTrace(); <br />
} <br />
} <br />
} <br />
熟悉 java 语言的程序员朋友们，你们认为上面的程序有什么问题吗？编译能通过吗？如果不能，那么原因又是为何呢？好了，有了自己的分析和预期之后，不妨亲自动手编译一下上面的小程序，呵呵！结果确实如您所料？是的，的确是编译时报错了，错误信息如下： <br />
E:\Trans.java:20: unreported exception java.lang.Exception; must be caught or declared to be thrown <br />
if (rd == null || wr == null) throw new Exception("error! test!"); <br />
1 error <br />
上面这种编译错误信息，相信 Java 程序员肯定见过（可能还是屡见不鲜！）！ <br />
相信老练一些的 Java 程序员一定非常清楚上述编译出错的原因。那就是如错误信息中（&#8220; must be caught &#8221;）描述的那样， 在 Java 的异常处理模型中，要求所有被抛出的异常都必须要有对应的&#8220;异常处理模块&#8221; 。也即是说，如果你在程序中 throw 出一个异常，那么在你的程序中（函数中）就必须要 catch 这个异常（处理这个异常）。例如上面的例子中，你在第 20 行代码处，抛出了一个 Exception 类型的异常，但是在该函数中，却没有 catch 并处理掉此异常的地方。因此，这样的程序即便是能够编译通过，那么运行时也是致命的（可能导致程序的崩溃），所以， Java 语言干脆在编译时就尽可能地检查（并卡住）这种本不应该出现的错误，这无疑对提高程序的可靠性大有帮助。 <br />
但是，在 Java 语言中，这就是必须的。 如果一个函数中，它运行时可能会向上层调用者函数抛出一个异常，那么，它就必须在该函数的声明中显式的注明（采用 throws 关键字） 。还记得刚才那条编译错误信息吗？&#8220; must be caught or declared to be thrown &#8221;，其中&#8220; must be caught &#8221;上面已经解释了，而后半部分呢？&#8220; declared to be thrown &#8221;是指何意呢？其实指的就是&#8220;必须显式地声明某个函数可能会向外部抛出一个异常&#8221;，也即是说，如果一个函数内部，它可能抛出了一种类型的异常，但该函数内部并不想（或不宜） catch 并处理这种类型的异常，此时，它就必须使用 throws 关键字来声明该函数可能会向外部抛出一个异常，以便于该函数的调用者知晓并能够及时处理这种类型的异常。下面列出了这几种情况的比较，代码如下： <br />
// 示例程序 1 ，这种写法能够编译通过 <br />
package com.ginger.exception;<br />
import java.io.*;<br />
public class Trans {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public static void main(String[] args) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (Exception ex) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static void test() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new Exception("To show Exception Successed");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (Exception ex) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
// 示例程序 2 ，这种写法就不能够编译通过 <br />
import java.io.*;<br />
public class Trans {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public static void main(String[] args) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 虽然这里能够捕获到 Exception 类型的异常<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;catch (Exception ex) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static void test() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new Exception("test");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
// 示例程序 3 ，这种写法又能够被编译通过 <br />
import java.io.*; <br />
public class Trans <br />
{ <br />
public static void main(String[] args) <br />
{ <br />
try <br />
{ <br />
test(); <br />
} <br />
catch(Exception ex) <br />
{ <br />
ex.printStackTrace(); <br />
} <br />
} <br />
// 由于函数声明了可能抛出 Exception 类型的异常 <br />
static void test() throws Exception <br />
{ <br />
throw new Exception("test"); <br />
} <br />
} <br />
// 示例程序 4 ，它又不能够被编译通过了 <br />
import java.io.*; <br />
public class Trans <br />
{ <br />
public static void main(String[] args) <br />
{ <br />
try <br />
{ <br />
// 虽然 test() 函数并没有真正抛出一个 Exception 类型的异常 <br />
// 但是由于它函数声明时，表示它可能抛出一个 Exception 类型的异常 <br />
// 所以，这里仍然不能被编译通过。 <br />
// 呵呵！体会到了 Java 异常处理模型的严谨吧！ <br />
test(); <br />
} <br />
catch(IOException ex) <br />
{ <br />
ex.printStackTrace(); <br />
} <br />
} <br />
static void test() throws Exception <br />
{ <br />
} <br />
} <br />
不知上面几个有联系的示例是否能够给大家带来&#8220;豁然开朗&#8221;的感觉，坦率的说， Java 提供的异常处理模型并不复杂，相信太多太多 Java 程序员有着比我更深刻的认识。最后，补充一种例外情况，请看如下代码： <br />
import java.io.*;<br />
public class Trans {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public static void main(String[] args) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (Exception ex) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static void test() throws Error {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new Error(" 故意抛出一个 Error");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
朋友们！上面的程序能被编译通过吗？注意，按照刚才上面所总结出的规律：在 Java 的异常处理模型中，要求所有被抛出的异常都必须要有对应的 catch 块！那么上面的程序肯定不能被编译通过，因为 Error 和 Exception 都是从 Throwable 直接派生而来，而 test 函数声明了它可能抛出 Error 类型的异常，但在 main 函数中却并没有 catch(Error) 或 catch(Throwable) 块，所以它理当是会编译出错的！真的吗？不妨试试！呵呵！结果并非我们之预料，而它恰恰是正确编译通过了。为何？ WHY ？ WHY ？ <br />
其实，原因很简单，那就是因为 Error 异常的特殊性。 Java 异常处理模型中规定： Error 和从它派生而来的所有异常，都表示系统中出现了一个非常严重的异常错误，并且这个错误可能是应用程序所不能恢复的（其实这在前面的内容中已提到过）。因此，如果系统中真的出现了一个 Error 类型的异常，那么则表明，系统已处于崩溃不可恢复的状态中，此时，作为编写 Java 应用程序的你，已经是没有必要（也没能力）来处理此等异常错误。所以， javac 编译器就没有必要来保证：&#8220;在编译时，所有的 Error 异常都有其对应的错误处理模块&#8221;。当然， Error 类型的异常一般都是由系统遇到致命的错误时所抛出的，它最后也由 Java 虚拟机所处理。而作为 Java 程序员的你，可能永远也不会考虑抛出一个 Error 类型的异常。因此 Error 是一个特例情况！ <br />
特别关注一下 RuntimeException <br />
上面刚刚讨论了一下 Error 类型的异常处理情况， Java 程序员一般无须关注它（处理这种异常）。另外，其实在 Exception 类型的异常对象中，也存在一种比较特别的&#8220;异常&#8221;类型，那就是 RuntimeException ，虽然它是直接从 Exception 派生而来，但是 Java 编译器（ javac ）对 RuntimeException 却是特殊待遇，而且是照顾有加。不信，看看下面的两个示例吧！代码如下： <br />
// 示例程序 1 <br />
// 它不能编译通过，我们可以理解 <br />
import java.io.*;<br />
<br />
public class Trans {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public static void main(String[] args) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static void test() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 注意这条语句<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new Exception(" 故意抛出一个 Exception");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
} <br />
// 示例程序 2 <br />
// 可它却为什么能够编译通过呢？ <br />
import java.io.*;<br />
<br />
public class Trans {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public static void main(String[] args) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static void test() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 注意这条语句<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new RuntimeException(" 故意抛出一个 RuntimeException");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
} <br />
对上面两个相当类似的程序， javac 编译时却遭遇了两种截然不同的处理，按理说，第 2 个示例程序也应该像第 1 个示例程序那样，编译时报错！但是 javac 编译它时，却例外地让它通过它，而且在运行时， java 虚拟机也捕获到了这个异常，并且会在 console 打印出详细的异常信息。运行结果如下： <br />
java.lang.RuntimeException: 故意抛出一个 RuntimeException <br />
at Trans.test(Trans.java:13) <br />
at Trans.main(Trans.java:8) <br />
Exception in thread "main" <br />
为什么对于 RuntimeException 类型的异常（以及从它派生而出的异常类型）， javac 和 java 虚拟机都特殊处理呢？要知道，这可是与&#8220; Java 异常处理模型更严谨和更安全&#8221;的设计原则相抵触的呀！究竟是为何呢？这简直让人不法理解呀！ <br />
只不过， Java 语言中， RuntimeException 被统一纳入到了 Java 语言和 JDK 的规范之中。请看如下代码，来验证一下我们的理解！ <br />
import java.io.*; <br />
public class Trans <br />
{ <br />
public static void main(String[] args) <br />
{ <br />
test(); <br />
} <br />
static void test() <br />
{ <br />
int i = 4; <br />
int j = 0; <br />
// 运行时，这里将触发了一个 ArithmeticException <br />
// ArithmeticException 从 RuntimeException 派生而来 <br />
System.out.println("i / j = " + i / j); <br />
} <br />
} <br />
运行结果如下： <br />
java.lang.ArithmeticException: / by zero <br />
at Trans.test(Trans.java:16) <br />
at Trans.main(Trans.java:8) <br />
Exception in thread "main" <br />
又如下面的例子，也会产生一个 RuntimeException ，代码如下： <br />
import java.io.*; <br />
public class Trans <br />
{ <br />
public static void main(String[] args) <br />
{ <br />
test(); <br />
} <br />
static void test() <br />
{ <br />
String str = null; <br />
// 运行时，这里将触发了一个 NullPointerException <br />
// NullPointerException 从 RuntimeException 派生而来 <br />
str.compareTo("abc"); <br />
} <br />
} <br />
所以，针对 RuntimeException 类型的异常， javac 是无法通过编译时的静态语法检测来判断到底哪些函数（或哪些区域的代码）可能抛出这类异常（这完全取决于运行时状态，或者说运行态所决定的），也正因为如此， Java 异常处理模型中的&#8220; must be caught or declared to be thrown &#8221;规则也不适用于 RuntimeException （所以才有前面所提到过的奇怪编译现象，这也属于特殊规则吧）。但是， Java 虚拟机却需要有效地捕获并处理此类异常。当然， RuntimeException 也可以被程序员显式地抛出，而且为了程序的可靠性，对一些可能出现&#8220;运行时异常（ RuntimeException ）&#8221;的代码区域，程序员最好能够及时地处理这些意外的异常，也即通过 catch(RuntimeExcetion) 或 catch(Exception) 来捕获它们。如下面的示例程序，代码如下： <br />
import java.io.*; <br />
public class Trans <br />
{ <br />
public static void main(String[] args) <br />
{ <br />
try <br />
{ <br />
test(); <br />
} <br />
// 在上层的调用函数中，最好捕获所有的 Exception 异常！ <br />
catch(Exception e) <br />
{ <br />
System.out.println("go here!"); <br />
e.printStackTrace(); <br />
} <br />
} <br />
// 这里最好显式地声明一下，表明该函数可能抛出 RuntimeException <br />
static void test() throws RuntimeException <br />
{ <br />
String str = null; <br />
// 运行时，这里将触发了一个 NullPointerException <br />
// NullPointerException 从 RuntimeException 派生而来 <br />
str.compareTo("abc"); <br />
} <br />
} <br />
</span></span></p>
<p class="diaryFoot"><span style="color: #000080"></span></p>
<img src ="http://www.blogjava.net/sutao/aggbug/161683.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sutao/" target="_blank">苏醄</a> 2007-11-19 17:51 <a href="http://www.blogjava.net/sutao/articles/161683.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java异常</title><link>http://www.blogjava.net/sutao/articles/161670.html</link><dc:creator>苏醄</dc:creator><author>苏醄</author><pubDate>Mon, 19 Nov 2007 09:21:00 GMT</pubDate><guid>http://www.blogjava.net/sutao/articles/161670.html</guid><wfw:comment>http://www.blogjava.net/sutao/comments/161670.html</wfw:comment><comments>http://www.blogjava.net/sutao/articles/161670.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sutao/comments/commentRss/161670.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sutao/services/trackbacks/161670.html</trackback:ping><description><![CDATA[<span><span style="color: #0010ff">原文出处：</span></span><a href="http://dev2dev.bea.com/pub/a/2006/11/effective-exceptions.html" target="_top"><span><span style="color: #0010ff">http://dev2dev.bea.com/pub/a/2006/11/effective-exceptions.html</span></span></a><span><span style="color: #0010ff"> </span></span>
<h3><span><span style="color: #0010ff">摘要</span></span></h3>
<p><span style="color: #0010ff">　　Java开发人员做出的有关架构的最重要的决定之一便是如何使用Java异常模型。Java异常处理成为社区中讨论最多的话题之一。一些人认为 Java语言中的已检查异常（Checked Exceptions）是一次失败的尝试。本文认为错误并不在于Java模型本身，而在于Java库设计人员没有认识到方法失败的两个基本原因。本文提倡 思考异常情况的本质，并描述了有助于用户设计的设计模式。最后，本文讨论了异常处理在面向方面编程（Aspect Oriented Programming）模型中作为横切关注点（crosscutting concern）的情况。如果使用得当，Java异常将对程序开发人员大有裨益。本文将帮助读者正确使用Java异常。 </span></p>
<h3><span><span style="color: #0010ff">为什么异常非常重要</span></span></h3>
<p><span><span style="color: #0010ff">　　Java应用程序中的异常处理可以告诉用户构建应用程序的架构强度。架构是指在应用程序的各个层面上所做出的并始终遵守的决策。其中最重要的决 策之一便是应用程序中类、子系统或层之间进行互相通信的方式。方法通过Java异常可以为操作传递另一种结果，因此应用程序架构特别值得我们去关注。</span></span></p>
<p><span><span style="color: #0010ff">　 　判断Java架构师技能的高低和开发团队是否训练有素，其中比较好的方法是查看应用程序中的异常处理代码。首先需要观察的是有多少代码专门用于捕捉异 常、记录异常、确定发生的事件和异常转化。简洁、紧凑和有条理的异常处理表明团队有使用Java异常的一致方法。当异常处理代码的数量将要超过其他方面的 代码时，可以断定团队成员之间的沟通已经打破（或者这种沟通从一开始就不存在），每个人都用自己的方法来处理异常。</span></span></p>
<p><span><span style="color: #0010ff">　　临时异常处理的结果 非常具有预见性。如果问团队成员为什么在代码的某个特定点丢弃、捕捉、或忽略某个异常，回答通常是：&#8220;除此之外，我不知道怎么做。&#8221;如果问他们在编写代码 的异常实际发生时会产生什么情况，他们通常会皱眉，回答类似于：&#8220;我不知道。我们从来没有测试过。&#8221;</span></span></p>
<p><span><span style="color: #0010ff">　　要判断Java组件是否有效地利用 了Java异常，可以查看其客户端的代码。如果客户端代码中包含大量计算操作失败时间、原因和处理方法的逻辑，原因几乎都是由于组件的错误报告设计。有缺 陷的报告会在客户端产生大量的&#8220;记录和遗留&#8221;（log and forget）代码，而没有任何用途。最糟糕的是扭曲的逻辑路径、互相嵌套的try/catch/finally程序块，以及其他导致脆弱和无法管理的应 用程序的混乱。</span></span></p>
<p><span><span style="color: #0010ff">　　事后处理异常（或者根本不处理）是造成软件项目混乱和延迟的主要原因。异常处理关系到软件设计的各个方面。为异常建立架构约定应该是项目中首先要做出的决定之一。合理使用Java异常模型将对保持应用程序的简洁性、可维护性和正确性大有帮助。 </span></span></p>
<h3><span><span style="color: #0010ff">挑战异常准则</span></span></h3>
<p><span><span style="color: #0010ff">　　如何正确使用Java异常模型已经成为大量讨论的主题。Java并不是支持异常语义的第一种语言；但是，通过Java编译器可强制使用规则来控 制如何声明和处理特定的异常。许多人认为编译时异常检查对精确软件设计有帮助，它与其他语言特征能够很好地协调起来。图1表明了Java异常的层次结构。</span></span></p>
<p><span><span style="color: #0010ff">　 　通常，Java编译器会根据java.lang.Throwable强制方法抛出异常，包括其声明中&#8220;throw&#8221;子句的异常。同样，编译器会验证方法 的客户端是捕获声明异常类型还是指定自己抛出异常类型。这些简单的规则对于全世界的Java开发人员产生了深远的影响。</span></span></p>
<p><span><span style="color: #0010ff">　　编译器针对 Throwable继承树的两个分支放宽了异常检查行为。java.lang.Error和java.lang.RuntimeException的子类 免于编译时检查。在两者中，软件设计人员通常对运行时异常更感兴趣。通常使用术语&#8220;未检查异常&#8221;（unchecked exception）来区分其他的所有&#8220;已检查异常&#8221;（checked exception）</span></span></p>
<p><span><span style="color: #0010ff">图 1 Java异常层次结构</span></span></p>
<p><span><span style="color: #0010ff">我认为已检查异常会受到那些重视Java语言中强类型的人的欢迎。毕竟，由编译器对数据类型产生的约束会鼓励严格编码和精确思维。编译时类型检查有 助于防止在运行时产生难以应付的意外事件。编译时异常检查将起到相同的效果，提醒开发人员注意的是，方法会有潜在的其他结果需要进行处理。</span></span></p>
<p><span><span style="color: #0010ff">　 　在早期，推荐无论何处都尽量使用已检查异常，以便充分借助编译器生产出无差错软件。Java API库的设计人员显然赞成已检查异常准则，并广泛使用这些异常来模仿在库方法中发生的任何意外事件。在J2SE 5.1 API规范中，已检查异常类型仍然比未检查异常类型多，比例要超过二比一。</span></span></p>
<p><span><span style="color: #0010ff">　　对于程序员来说，Java类库中的绝大多数公共方法好像为每一个可能的失败都声明了已检查异常。例如，java.io包对已检查异常IOException的依赖性特别大。至少63个Java库包直接或间接通过其数十个子类之一发布了此异常。</span></span></p>
<p><span><span style="color: #0010ff">　 　I/O失败很严重但也很少见。另外，程序代码通常没有能力从失败中恢复。Java程序员发现他们必须提供IOException和类似在简单的Java 库方法调用时可能发生的不可恢复的事件。捕捉这些异常只会打乱原有简洁的代码，因为捕捉块并不能改善此类情况。而不捕捉这些异常可能会更糟糕，因为编译器 要求将这些异常加入到方法所抛出的异常列表中。这公开了一些实现细节，优秀的面向对象设计自然会将这些细节隐藏起来。</span></span></p>
<p><span><span style="color: #0010ff">　　这种无法成功的情况导致许多严重违反Java编程模式的异常处理。当今的程序员经常被告诫要提防这些情况。同样，在创建工作区方面也产生大量正确和错误的建议。</span></span></p>
<p><span><span style="color: #0010ff">　　一些Java天才开始质疑Java已检查异常模型是否是一次失败的尝试。可以确定某个地方出了问题，但是这和Java语言中的异常检查无关。失败的原因是Java API设计人员的思考方式，即他们认为大多数失败情况都相同，并且可以用相同类型的异常来传达。 </span></span></p>
<h3><span><span style="color: #0010ff">错误和意外事件</span></span></h3>
<p><span><span style="color: #0010ff">　　假想金融应用软件中的CheckingAccount类。CheckingAccount属于客户，维护当前余额，并且可以接受存款，根据支票 接受终止支付命令，以及处理入帐的支票。CheckingAccount对象必须协调并发线程的访问，因为每一个线程都可以改变它的状态。 CheckingAccount的processCheck()方法将Check对象作为参数，从账户余额中正常扣除支票金额。但是调用 processCheck()的支票结算客户端必须为两类意外事件做好准备。首先，CheckingAccount 可能有为支票注册的终止支付命令。第二，账户中可能没有足够的资金来支付支票金额。</span></span></p>
<p><span><span style="color: #0010ff">　　所以，processCheck()方法使用三种可 能的方式来响应其调用者。正常的响应方式是支票得到处理，在方法签名中声明的结果返回给调用服务。这两类意外事件响应代表了金融领域非常真实的情况，它们 需要与支票结算客户端进行通信。所有这三种processCheck()响应都是为模仿典型的支票账户行为而精心设计的。</span></span></p>
<p><span><span style="color: #0010ff">　　在Java中 表示意外事件响应的通常方法是定义两种异常，即StopPaymentException和InsufficientFundsException。客户 端忽略这两个异常是不正确的，因为在应用程序正常操作时会被抛出这两个异常。这两个异常有助于表达方法的所有行为，和方法签名一样十分重要。</span></span></p>
<p><span><span style="color: #0010ff">　　客户端可以轻松地处理这两类异常。如果终止支票的支付，客户端可以取得支票进行特殊处理。如果没有足够的资金，为支付此支票，客户端将从客户的储蓄帐户中转移资金，并重新尝试。</span></span></p>
<p><span><span style="color: #0010ff">　　这些意外事件可以预见，它是使用CheckingAccount API的自然结果。它们并不表示软件或执行环境的失败。将这些异常条件与实际的失败对比，实际的失败是由于CheckingAccount类的内部实现细节问题造成的。</span></span></p>
<p><span><span style="color: #0010ff">　 　假设CheckingAccount在数据库中维持持久的状态并使用JDBC API进行访问。在该API中，几乎每一个数据库访问方法都有失败的可能性，但原因与CheckingAccount实现无关。例如，有人会忘记打开数据 库服务器、不小心拔下了网线，或改变了访问数据库的密码。</span></span></p>
<p><span><span style="color: #0010ff">　　JDBC依靠单独的已检查异常SQLException来报告一切可能的错 误。大多数错误都与数据库的配置、连接和硬件设备有关。processCheck()方法并不能以有意义的方式处理这些异常条件。很遗憾，因为 processCheck()至少知道它自己的实现方式。调用堆栈中的上游方法能够处理这些问题的可能性会更小。</span></span></p>
<p><span><span style="color: #0010ff">　　CheckingAccount示例解释了导致方法执行不能返回预期结果的两个基本原因。下面首先介绍一些描述性术语：</span></span></p>
<h3><span><span style="color: #0010ff">意外事件</span></span></h3>
<p><span><span style="color: #0010ff">　　 是一种可以预见的情况，要求方法做出某种响应，以便能够表达方法所期望的目的。方法的调用者预见这些情况并采取策略应付这些情况。</span></span></p>
<h3><span><span style="color: #0010ff">错误</span></span></h3>
<p><span><span style="color: #0010ff">　　 是一种计划外的情况，它阻止方法达到其预期目的，并且如果不引用方法的内部实现，则无法描述这种情况。</span></span></p>
<p><span><span style="color: #0010ff">　　从这两个术语来看，终止支付命令和透支是processCheck()方法两个可能的意外事件。SQL问题代表了可能的错误异常条件。processCheck()的调用者应当提供一种处理意外事件的方式，但当错误发生时，并不能合理地处理该错误。 </span></span></p>
<h3><span><span style="color: #0010ff">映射Java异常</span></span></h3>
<p><span><span style="color: #0010ff">　　对于意外事件和错误，思考其原因将有助于为应用程序架构中的Java异常建立约定。 </span></span></p>
<table class="tabel-general" cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <th class="left"><span><span style="color: #0010ff">异常条件</span></span></th>
            <th class="center"><span><span style="color: #0010ff">意外事件</span></span></th>
            <th class="right"><span><span style="color: #0010ff">错误</span></span></th>
        </tr>
        <tr>
            <td class="left"><span><span style="color: #0010ff">认为是（Is considered to be）</span></span></td>
            <td class="center"><span><span style="color: #0010ff">设计的一部分</span></span></td>
            <td class="right"><span><span style="color: #0010ff">难以应付的意外</span></span></td>
        </tr>
        <tr>
            <td class="left"><span><span style="color: #0010ff">预期发生（Is expected to happen）</span></span></td>
            <td class="center"><span><span style="color: #0010ff">有规律但很少发生</span></span></td>
            <td class="right"><span><span style="color: #0010ff">从不</span></span></td>
        </tr>
        <tr>
            <td class="left"><span><span style="color: #0010ff">谁来处理（Who cares about it）</span></span></td>
            <td class="center"><span><span style="color: #0010ff">调用方法的上游代码</span></span></td>
            <td class="right"><span><span style="color: #0010ff">需要修复此问题的人员</span></span></td>
        </tr>
        <tr>
            <td class="left"><span><span style="color: #0010ff">实例（Examples）</span></span></td>
            <td class="center"><span><span style="color: #0010ff">另一种返回模式</span></span></td>
            <td class="right"><span><span style="color: #0010ff">编程缺陷，硬件故障，配置错误，文件丢失，服务器无法使用</span></span></td>
        </tr>
        <tr>
            <td class="left"><span><span style="color: #0010ff">最佳映射（Best Mapping）</span></span></td>
            <td class="center"><span><span style="color: #0010ff">已检查异常</span></span></td>
            <td class="right"><span><span style="color: #0010ff">未检查异常</span></span></td>
        </tr>
    </tbody>
</table>
<p><span><span style="color: #0010ff">　　意外事件异常条件完美地映射到Java已检查异常。由于它们是方法语义契约中不可或缺的一部分，因此必须借助编译器来确保问题得到了处理。如果 开发人员坚持在编译器有问题时处理或声明意外事件异常，此时编译器会成为一种阻碍，可以断定此软件设计必须进行部分重构。这其实是一件好事。</span></span></p>
<p><span><span style="color: #0010ff">　 　错误条件对编程人员来说能够引起关注，而对于软件逻辑却并非如此。&#8220;软件诊断学家&#8221;收集错误信息以诊断和修复引起错误发生的根源。因此，未检查Java 异常是错误的完美表现方式，它们可以使错误通知完整地过滤调用堆栈上的所有方法，传递到专门用于捕捉错误的层，捕获其中所包含的诊断信息，并为此活动提供 一份受约束的合理结论。错误产生方法并不需要声明，上游代码也不需要捕获它们，方法的实现得到了有效的隐藏——产生最少的代码混乱。</span></span></p>
<p><span><span style="color: #0010ff">　　较 新的Java API（比如Spring Framework和Java Data Object库）很少或根本不依赖于已检查异常。Hibernate ORM framework从release 3.0起重新定义了关键设备，以免于使用已检查异常。这反映了由这些框架报告的绝大部分异常异常条件是不可恢复的，这些异常源于方法调用的不正确编码或数 据库服务器失效等基本组件原因。实际上，强制调用者去捕捉或声明这样异常几乎没有任何益处。</span></span></p>
<h3><span><span style="color: #0010ff">架构中的错误处理</span></span></h3>
<p><span><span style="color: #0010ff">　　 在架构中有效处理错误的第一步是承认处理错误的必要性。承认这一点对于工程师来说有困难，因为他们认为自己有能力创造无缺陷的软件，并引以为豪。下面这些 理由可能有所帮助。首先，应用程序开发会在常见错误上花费大量的时间。提供程序员产生的错误将使团队诊断和修复这些错误变得十分简单。第二，对于错误异常 条件过度使用Java库中的已检查异常将强制代码来处理这些错误，即使调用顺序完全正确。如果没有适当的错误处理框架，由此产生的暂时异常处理将向应用程 序中插入平均信息量。</span></span></p>
<p><span><span style="color: #0010ff">　　成功的错误处理框架必须达到四个目标： </span></span></p>
<ul>
    <li><span><span style="color: #0010ff">使代码混乱最小化 </span></span>
    <li><span><span style="color: #0010ff">捕捉并保留诊断信息 </span></span>
    <li><span><span style="color: #0010ff">通知合适的人员 </span></span>
    <li><span><span style="color: #0010ff">比较得体地退出活动 </span></span></li>
</ul>
<p><span><span style="color: #0010ff">　　错误会分散应用程序的真正目的。因此，用于错误处理的代码数量应当最小化，并在理想情况下，应与程序的语义部分隔离。错误处理必须满足纠错人员 的需要。他们需要知道是否发生错误并且获取相关信息以判断错误原因。尽管从定义上说，错误不可恢复，但可靠的错误处理措施将以得体地方式终结出现错误的活 动。</span></span></p>
<p><span><span style="color: #0010ff">　</span></span></p>
<h3><span><span style="color: #0010ff">对于错误异常条件使用未检查异常</span></span></h3>
<p><span><span style="color: #0010ff">　　 有许多原因使我们做出使用未检查异常表示错误异常条件的架构性决定。作为对编程错误的回报，Java运行时将抛出RuntimeException的子类 （比如ArithmeticException和ClassCastException），针对架构设定先例。未检查异常使上游方法摆脱了包含无关代码的 要求，从而最大限度地减少了混乱。</span></span></p>
<p><span><span style="color: #0010ff">　　错误处理策略应当承认Java库和其他API中的方法可能使用已检查异常来表示应用程序环境下的错误异常条件。在这种情况下，采用架构惯例在其出现的地方捕捉API异常，将它作为错误，并抛出未检查异常来说明错误异常条件并捕捉诊断信息。</span></span></p>
<p><span><span style="color: #0010ff">　 　在这种情况下抛出的特定异常类型应当由架构本身定义。不要忘记错误异常的主要目的是传达诊断信息并记录，以帮助开发人员发现问题产生的原因。使用多错误 异常类型可能有些过度，因为架构会对它们进行完全相同的处理。在绝大多数情况下，把良好的描述性文本消息嵌入到单独的错误类型中，便可完成此项工作。使用 Java的一般RuntimeException来表示错误条件很容易进行防御。从Java 1.4时起，RuntimeException同其他throwable类一样，支持异常处理链式机制，允许设计人员捕捉并报告导致错误的已检查异常。</span></span></p>
<p><span><span style="color: #0010ff">　 　设计人员可以定义自己的未检查异常进行报告错误。如果需要使用不支持异常链接机制的Java 1.3或更早版本，这一步是必需的。实现相似的链接功能去捕捉并转换引起应用程序错误的异常相当简单。在错误报告异常中，应用程序可能需要特别的行为。这 可能是为架构创建RuntimeException子类的另一个原因。</span></span></p>
<h3><span><span style="color: #0010ff">建立错误屏障</span></span></h3>
<p><span><span style="color: #0010ff">　　 决定哪些异常要抛出以及何时抛出将成为错误处理框架的重要决定。同样重要的问题是何时捕捉错误异常及其后如何做。这里的目标是使应用程序的功能部分从处理错误的职责中分离出来。关注点分离通常是比较好的做法。负责处理错误的中央设备将为您带来很多的好处。</span></span></p>
<p><span><span style="color: #0010ff">　 　在错误屏障（fault barrier）模式下，任何应用程序组件都可以抛出错误异常，但只有作为&#8220;错误屏障&#8221;的组件才可以捕捉到错误异常。开发人员为了处理错误问题在应用程序 中插入了大量复杂代码，而采用此模式可消除大部分此类代码。从逻辑上讲，错误屏障存在于靠近调用堆栈的顶端。在这里，它可以阻断异常向上传播，以避免触发 默认动作。默认动作根据应用程序类型的不同而不同。对于独立的Java应用程序来说，默认动作意味着终止活动线程。对于驻留在应用服务器上的Web应用程 序，默认动作意味着应用服务器会向浏览器发送不友好的（且令人为难的）响应。</span></span></p>
<p><span><span style="color: #0010ff">　　错误屏障组件的首要职责是记录包含在错误异常中的信息，以 便进行下一步行动。应用程序日志是迄今为止做这件事情最理想的方法。异常的链信息、堆栈跟踪等对于诊断专家来说都是有价值的信息。发送错误信息最差的地方 是通过用户界面。将客户牵涉到应用程序的排错过程中，将对开发人员或客户没有任何益处。如果开发人员真的把诊断信息添加到了用户界面上，这说明开发人员的 记录策略需要改进。</span></span></p>
<p><span><span style="color: #0010ff">　　错误屏障的下一个职责是以受控方式停止操作。这个职责的含义由应用程序的设计决定，但是通常会涉及到为等待响应的客 户端发出总体响应。如果应用程序是Web service，这意味着使用soap:Server的<faultcode></faultcode>和普通<faultstring></faultstring>失败消息将 SOAP <fault></fault>元素嵌入到响应中。如果应用程序与Web浏览器进行通信，屏障将安排发送普通的HTML响应，表示无法处理此请求。</span></span></p>
<p><span><span style="color: #0010ff">　 　在Struts应用程序中，错误屏障采用全局异常处理程序的形式，配置成可以处理RuntimeException的任何子类。错误屏障类将扩展 org.apache.struts.action.ExceptionHandler，在需要实现自定义处理时重写方法。这将处理由于疏忽产生的错误条 件和处理Struts操作中明显发现的错误条件。图2显示了意外事件异常和错误异常。</span></span></p>
<p><span><span style="color: #0010ff">图2 意外事件异常和错误异常</span></span></p>
<span><span style="color: #0010ff">如果开发人员正在使用Spring MVC框架，简单地扩展SimpleMappingExceptionResolver并进行配置使其能处理RuntimeExceptio及其子类，便 能建立起错误屏障。通过重写resolveException()方法，在使用超类方法向发送普通错误显示的查看组件发出请求之前，开发人员可以添加任何 自定义处理。 </span></span>
<p><span><span style="color: #0010ff">　　当架构包含错误屏障并且开发人员也意识到了它的存在时，编写一劳永逸的错误异常处理代码的吸引力急剧下降。结果是在应用程序中产生更简洁和更易维护的代码。 </span></span></p>
<h3><span><span style="color: #0010ff">架构中的意外事件处理</span></span></h3>
<p><span><span style="color: #0010ff">　　随着错误处理委托给屏障，主要组件之间的意外事件通信变得更加简单。意外事件代表了另一种方法结果，此结果与主要返回结果同样重要。因此，已检 查异常类型是传递意外事件条件存在性并提供对付异常条件所需信息的良好工具。最佳实践是借助Java编译器来提醒开发人员他们正在使用API的所有方面， 同样需要提供方法结果的全部范围。</span></span></p>
<p><span><span style="color: #0010ff">　　通过单独使用方法的返回类型，可以传递简单的意外事件。例如，返回null引用而非实际对象可以说明 此对象由于明确的原因而无法创建。Java I/O方法通常返回整数值-1，而不是字节值或字节计数，用来表明文件的结束。如果方法的语义非常简单允许这样做，另一种返回值可以使用这种方式，因为它 们消除了由异常而带来的开销。不利方面是方法调用者负责检测返回值，来查看它是主要结果还是意外事件结果。然而，编译器并不强制调用者做这样的测试。</span></span></p>
<p><span><span style="color: #0010ff">　 　如果方法具有void返回类型，异常将是表明意外事件发生的唯一方法。如果方法返回对象引用，则返回值所表达的意思仅限于两个值（null和non- null）。如果方法返回整数值，通过选择确保与主要返回值不相冲冲突的值，就可以表达数个意外事件条件。但是现在已经进入了错误代码检查的范畴，这是 Java异常模型需要避免的情况。</span></span></p>
<h3><span><span style="color: #0010ff">提供有用的信息</span></span></h3>
<p><span><span style="color: #0010ff">　　 定义不同的错误报告异常类型没有任何道理，因为错误屏障会对它们进行同样的处理。意外事件异常差异很大，因为它们会向方法调用者传达各种条件。您的架构可能明确指定这些异常都应该扩展java.lang.Exception或指定的基类。</span></span></p>
<p><span><span style="color: #0010ff">　 　不要忘记这些异常是完整的Java类型，可以调整特定的字段、方法以及为特殊目的而构建的构造函数。例如，假想的CheckingAccount processCheck()方法抛出的InsufficientFundsException类型可能包括OverdraftProtection对 象，此对象能够转移资金以弥补另一个账户的资金短缺，此账户的身份取决于设置核算账户的方式。</span></span></p>
<h3><span><span style="color: #0010ff">记录还是不记录</span></span></h3>
<p><span><span style="color: #0010ff">　　 记录错误异常有实际意义是因为它们的目的是吸引开发人员去注意需要纠正的情况。但这并不适用于意外事件异常。它们可能代表相对少见的事件，但是在应用程序 的生命周期内，这些意外事件依然会发生。它们表明了如发生异常应用程序将按照其设计意图进行工作。按照惯例，在意外事件捕捉模块中加入记录代码只会增加混 乱代码而没有任何益处。如果意外事件表示重要事件，最好为方法产生一条记录项，用于在抛出意外事件异常并通知其调用者之前记录此事件。 </span></span></p>
<h3><span><span style="color: #0010ff">异常方面</span></span></h3>
<p><span><span style="color: #0010ff">　　在面向方面编程（Aspect Oriented Programming (AOP)）中，错误和意外事件的处理是横切关注点。例如，要实现错误屏障模式，所有参与的类都必须遵守公共约定： </span></span></p>
<ul>
    <li><span><span style="color: #0010ff">错误屏障方法必须驻留在遍历参与类的方法调用的头部。 </span></span>
    <li><span><span style="color: #0010ff">它们都必须使用未检查异常来表示错误条件。 </span></span>
    <li><span><span style="color: #0010ff">它们都必须使用特定的未检查异常类型，以便错误屏障能够接收到。 </span></span>
    <li><span><span style="color: #0010ff">它们都必须从较低层方法中捕捉并转换已检查异常，这些异常在它们的执行环境中被视为错误。 </span></span>
    <li><span><span style="color: #0010ff">它们不能干扰错误异常到屏障的传播。 </span></span></li>
</ul>
<p><span><span style="color: #0010ff">　　这些关注点跨越了其他不相关类的边界。结果产生了少量分散错误处理代码并致使屏障类与参与者之间的隐式耦合（尽管对于完全没有使用模式来说是一 次重大改进）。AOP允许将错误处理关注点封装到应用于参与类的公共Aspect中。Java AOP框架（比如AspectJ和Spring AOP）把异常处理作为联接点，错误处理行为（或建议）能够附加到上面。这样，在错误屏障模式中绑定参与者的惯例就有所放宽。错误处理现在可以存在于独立 的非内联方面（out-of-line aspect）中，避免了将&#8220;屏障&#8221;方法置于方法调用序列的前面。</span></span></p>
<p><span><span style="color: #0010ff">　　如果开发人员在架构中使用AOP，错误和意外事件的处理是方面在整个应用程序中应用的理想候选对象。完全探究错误和意外事件处理在AOP中如何运作，这是个令人感兴趣的话题，留作以后讨论。 </span></span></p>
<h3><span><span style="color: #0010ff">结束语</span></span></h3>
<p><span><span style="color: #0010ff">　　尽管Java异常模型在其生命周期内已经引发了激烈的争论，但是当Java异常模型运用得当时，将会带来巨大的价值。作为架构师，应当决定如何 建立最大限度利用模型的惯例。思考一下错误和意外事件异常能够帮助开发人员做出正确的选择。Java异常模型使用得当，将保持应用程序的简洁性、可维护性 和正确性。把面向方面编程技术的错误和意外事件处理作为横切关注点，可为应用程序的架构带来某些明显的优势。 </span></span></p>
<img src ="http://www.blogjava.net/sutao/aggbug/161670.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sutao/" target="_blank">苏醄</a> 2007-11-19 17:21 <a href="http://www.blogjava.net/sutao/articles/161670.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java异常处理陋习- -</title><link>http://www.blogjava.net/sutao/articles/161668.html</link><dc:creator>苏醄</dc:creator><author>苏醄</author><pubDate>Mon, 19 Nov 2007 09:16:00 GMT</pubDate><guid>http://www.blogjava.net/sutao/articles/161668.html</guid><wfw:comment>http://www.blogjava.net/sutao/comments/161668.html</wfw:comment><comments>http://www.blogjava.net/sutao/articles/161668.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sutao/comments/commentRss/161668.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sutao/services/trackbacks/161668.html</trackback:ping><description><![CDATA[<div id="header">&nbsp;</div>
<div class="entity">
<h2 class="diaryTitle"><span style="color: #0000ff">java异常处理陋习- -</span></h2>
<p><span style="color: #0000ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>
<p>
<p><span style="color: #0000ff">Java异常处理之陋习展播</span></p>
<p><span style="color: #0000ff">　　你觉得自己是一个Java专家吗？是否肯定自己已经全面掌握了Java的异常处理机制？在下面这段代码中，你能够迅速找出异常处理的六个问题吗？ </span></p>
<p><span style="color: #0000ff">1 OutputStreamWriter out = ...<br />
2 java.sql.Connection conn = ...<br />
3 try { // ⑸<br />
4 Statement stat = conn.createStatement();<br />
5 ResultSet rs = stat.executeQuery(<br />
6 "select uid, name from user");<br />
7 while (rs.next())<br />
8 {<br />
9 out.println("ID：" + rs.getString("uid") // ⑹<br />
10 "，姓名：" + rs.getString("name"));<br />
11 }<br />
12 conn.close(); // ⑶<br />
13 out.close();<br />
14 }<br />
15 catch(Exception ex) // ⑵<br />
16 {<br />
17 ex.printStackTrace(); //⑴，⑷<br />
18 }</span></p>
<p><span style="color: #0000ff">　　作为一个Java程序员，你至少应该能够找出两个问题。但是，如果你不能找出全部六个问题，请继续阅读本文。 </span></p>
<p><span style="color: #0000ff">　　本文讨论的不是Java异常处理的一般性原则，因为这些原则已经被大多数人熟知。我们要做的是分析各种可称为&#8220;反例&#8221;（anti-pattern）的违背优秀编码规范的常见坏习惯，帮助读者熟悉这些典型的反面例子，从而能够在实际工作中敏锐地察觉和避免这些问题。 </span></p>
<p><span style="color: #0000ff">　　反例之一：丢弃异常 </span></p>
<p><span style="color: #0000ff">　　代码：15行-18行。 </span></p>
<p><span style="color: #0000ff">　　这段代码捕获了异常却不作任何处理，可以算得上Java编程中的杀手。从问题出现的频繁程度和祸害程度来看，它也许可以和C/C++程序的一个恶名远播的问题相提并论——不检查缓冲区是否已满。如果你看到了这种丢弃（而不是抛出）异常的情况，可以百分之九十九地肯定代码存在问题（在极少数情况下，这段代码有存在的理由，但最好加上完整的注释，以免引起别人误解）。 </span></p>
<p><span style="color: #0000ff">　　这段代码的错误在于，异常（几乎）总是意味着某些事情不对劲了，或者说至少发生了某些不寻常的事情，我们不应该对程序发出的求救信号保持沉默和无动于衷。调用一下printStackTrace算不上&#8220;处理异常&#8221;。不错，调用printStackTrace对调试程序有帮助，但程序调试阶段结束之后，printStackTrace就不应再在异常处理模块中担负主要责任了。 </span></p>
<p><span style="color: #0000ff">　　丢弃异常的情形非常普遍。打开JDK的ThreadDeath类的文档，可以看到下面这段说明：&#8220;特别地，虽然出现ThreadDeath是一种&#8216;正常的情形&#8217;，但ThreadDeath类是Error而不是Exception的子类，因为许多应用会捕获所有的Exception然后丢弃它不再理睬。&#8221;这段话的意思是，虽然ThreadDeath代表的是一种普通的问题，但鉴于许多应用会试图捕获所有异常然后不予以适当的处理，所以JDK把ThreadDeath定义成了Error的子类，因为Error类代表的是一般的应用不应该去捕获的严重问题。可见，丢弃异常这一坏习惯是如此常见，它甚至已经影响到了Java本身的设计。 </span></p>
<p><span style="color: #0000ff">　　那么，应该怎样改正呢？主要有四个选择： </span></p>
<p><span style="color: #0000ff">　　1、处理异常。针对该异常采取一些行动，例如修正问题、提醒某个人或进行其他一些处理，要根据具体的情形确定应该采取的动作。再次说明，调用printStackTrace算不上已经&#8220;处理好了异常&#8221;。 </span></p>
<p><span style="color: #0000ff">　　2、重新抛出异常。处理异常的代码在分析异常之后，认为自己不能处理它，重新抛出异常也不失为一种选择。 </span></p>
<p><span style="color: #0000ff">　　3、把该异常转换成另一种异常。大多数情况下，这是指把一个低级的异常转换成应用级的异常（其含义更容易被用户了解的异常）。 </span></p>
<p><span style="color: #0000ff">　　4、不要捕获异常。 </span></p>
<p><span style="color: #0000ff">　　结论一：既然捕获了异常，就要对它进行适当的处理。不要捕获异常之后又把它丢弃，不予理睬。 </span></p>
<p><span style="color: #0000ff">　　反例之二：不指定具体的异常 </span></p>
<p><span style="color: #0000ff">　　代码：15行。 </span></p>
<p><span style="color: #0000ff">　　许多时候人们会被这样一种&#8220;美妙的&#8221;想法吸引：用一个catch语句捕获所有的异常。最常见的情形就是使用catch(Exception ex)语句。但实际上，在绝大多数情况下，这种做法不值得提倡。为什么呢？ </span></p>
<p><span style="color: #0000ff">　　要理解其原因，我们必须回顾一下catch语句的用途。catch语句表示我们预期会出现某种异常，而且希望能够处理该异常。异常类的作用就是告诉Java编译器我们想要处理的是哪一种异常。由于绝大多数异常都直接或间接从java.lang.Exception派生，catch(Exception ex)就相当于说我们想要处理几乎所有的异常。 </span></p>
<p><span style="color: #0000ff">　　再来看看前面的代码例子。我们真正想要捕获的异常是什么呢？最明显的一个是SQLException，这是JDBC操作中常见的异常。另一个可能的异常是IOException，因为它要操作OutputStreamWriter。显然，在同一个catch块中处理这两种截然不同的异常是不合适的。如果用两个catch块分别捕获SQLException和IOException就要好多了。这就是说，catch语句应当尽量指定具体的异常类型，而不应该指定涵盖范围太广的Exception类。 </span></p>
<p><span style="color: #0000ff">　　另一方面，除了这两个特定的异常，还有其他许多异常也可能出现。例如，如果由于某种原因，executeQuery返回了null，该怎么办？答案是让它们继续抛出，即不必捕获也不必处理。实际上，我们不能也不应该去捕获可能出现的所有异常，程序的其他地方还有捕获异常的机会——直至最后由JVM处理。 </span></p>
<p><span style="color: #0000ff">　　结论二：在catch语句中尽可能指定具体的异常类型，必要时使用多个catch。不要试图处理所有可能出现的异常。 </span></p>
<p><span style="color: #0000ff">　　反例之三：占用资源不释放 </span></p>
<p><span style="color: #0000ff">　　代码：3行-14行。 </span></p>
<p><span style="color: #0000ff">　　异常改变了程序正常的执行流程。这个道理虽然简单，却常常被人们忽视。如果程序用到了文件、Socket、JDBC连接之类的资源，即使遇到了异常，也要正确释放占用的资源。为此，Java提供了一个简化这类操作的关键词finally。 </span></p>
<p><span style="color: #0000ff">　　finally是样好东西：不管是否出现了异常，Finally保证在try/catch/finally块结束之前，执行清理任务的代码总是有机会执行。遗憾的是有些人却不习惯使用finally。 </span></p>
<p><span style="color: #0000ff">　　当然，编写finally块应当多加小心，特别是要注意在finally块之内抛出的异常——这是执行清理任务的最后机会，尽量不要再有难以处理的错误。 </span></p>
<p><span style="color: #0000ff">　　结论三：保证所有资源都被正确释放。充分运用finally关键词。 </span></p>
<p><span style="color: #0000ff">　　反例之四：不说明异常的详细信息 </span></p>
<p><span style="color: #0000ff">　　代码：3行-18行。 </span></p>
<p><span style="color: #0000ff">　　仔细观察这段代码：如果循环内部出现了异常，会发生什么事情？我们可以得到足够的信息判断循环内部出错的原因吗？不能。我们只能知道当前正在处理的类发生了某种错误，但却不能获得任何信息判断导致当前错误的原因。 </span></p>
<p><span style="color: #0000ff">　　printStackTrace的堆栈跟踪功能显示出程序运行到当前类的执行流程，但只提供了一些最基本的信息，未能说明实际导致错误的原因，同时也不易解读。 </span></p>
<p><span style="color: #0000ff">　　因此，在出现异常时，最好能够提供一些文字信息，例如当前正在执行的类、方法和其他状态信息，包括以一种更适合阅读的方式整理和组织printStackTrace提供的信息。 </span></p>
<p><span style="color: #0000ff">　　结论四：在异常处理模块中提供适量的错误原因信息，组织错误信息使其易于理解和阅读。 </span></p>
<p><span style="color: #0000ff">　　反例之五：过于庞大的try块 </span></p>
<p><span style="color: #0000ff">　　代码：3行-14行。 </span></p>
<p><span style="color: #0000ff">　　经常可以看到有人把大量的代码放入单个try块，实际上这不是好习惯。这种现象之所以常见，原因就在于有些人图省事，不愿花时间分析一大块代码中哪几行代码会抛出异常、异常的具体类型是什么。把大量的语句装入单个巨大的try块就象是出门旅游时把所有日常用品塞入一个大箱子，虽然东西是带上了，但要找出来可不容易。 </span></p>
<p><span style="color: #0000ff">　　一些新手常常把大量的代码放入单个try块，然后再在catch语句中声明Exception，而不是分离各个可能出现异常的段落并分别捕获其异常。这种做法为分析程序抛出异常的原因带来了困难，因为一大段代码中有太多的地方可能抛出Exception。 </span></p>
<p><span style="color: #0000ff">　　结论五：尽量减小try块的体积。 </span></p>
<p><span style="color: #0000ff">　　反例之六：输出数据不完整 </span></p>
<p><span style="color: #0000ff">　　代码：7行-11行。 </span></p>
<p><span style="color: #0000ff">　　不完整的数据是Java程序的隐形杀手。仔细观察这段代码，考虑一下如果循环的中间抛出了异常，会发生什么事情。循环的执行当然是要被打断的，其次，catch块会执行——就这些，再也没有其他动作了。已经输出的数据怎么办？使用这些数据的人或设备将收到一份不完整的（因而也是错误的）数据，却得不到任何有关这份数据是否完整的提示。对于有些系统来说，数据不完整可能比系统停止运行带来更大的损失。 </span></p>
<p><span style="color: #0000ff">　　较为理想的处置办法是向输出设备写一些信息，声明数据的不完整性；另一种可能有效的办法是，先缓冲要输出的数据，准备好全部数据之后再一次性输出。 </span></p>
<p><span style="color: #0000ff">　　结论六：全面考虑可能出现的异常以及这些异常对执行流程的影响。 </span></p>
<p><span style="color: #0000ff">　　改写后的代码 </span></p>
<p><span style="color: #0000ff">　　根据上面的讨论，下面给出改写后的代码。也许有人会说它稍微有点啰嗦，但是它有了比较完备的异常处理机制。 </span></p>
<p><span style="color: #0000ff">OutputStreamWriter out = ...<br />
java.sql.Connection conn = ...<br />
try {<br />
Statement stat = conn.createStatement();<br />
ResultSet rs = stat.executeQuery(<br />
"select uid, name from user");<br />
while (rs.next())<br />
{<br />
out.println("ID：" + rs.getString("uid") +<br />
"，姓名: " + rs.getString("name"));<br />
}<br />
}<br />
catch(SQLException sqlex)<br />
{<br />
out.println("警告：数据不完整");<br />
throw new ApplicationException(<br />
"读取数据时出现SQL错误", sqlex);<br />
}<br />
catch(IOException ioex)<br />
{<br />
throw new ApplicationException(<br />
"写入数据时出现IO错误", ioex);<br />
}<br />
finally<br />
{<br />
if (conn != null) {<br />
try {<br />
conn.close();<br />
}<br />
catch(SQLException sqlex2)<br />
{<br />
System.err(this.getClass().getName() +<br />
".mymethod - 不能关闭数据库连接: " +<br />
sqlex2.toString());<br />
}<br />
}</span></p>
<p><span style="color: #0000ff">if (out != null) {<br />
try {<br />
out.close();<br />
}<br />
catch(IOException ioex2)<br />
{<br />
System.err(this.getClass().getName() +<br />
".mymethod - 不能关闭输出文件" +<br />
ioex2.toString());<br />
}<br />
}<br />
}</span></p>
<p><span style="color: #0000ff">　　本文的结论不是放之四海皆准的教条，有时常识和经验才是最好的老师。如果你对自己的做法没有百分之百的信心，务必加上详细、全面的注释。 </span></p>
<p><span style="color: #0000ff">　　另一方面，不要笑话这些错误，不妨问问你自己是否真地彻底摆脱了这些坏习惯。即使最有经验的程序员偶尔也会误入歧途，原因很简单，因为它们确确实实带来了&#8220;方便&#8221;。所有这些反例都可以看作Java编程世界的恶魔，它们美丽动人，无孔不入，时刻诱惑着你。也许有人会认为这些都属于鸡皮蒜毛的小事，不足挂齿，但请记住：勿以恶小而为之，勿以善小而不为。 </span></p>
</div>
<img src ="http://www.blogjava.net/sutao/aggbug/161668.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sutao/" target="_blank">苏醄</a> 2007-11-19 17:16 <a href="http://www.blogjava.net/sutao/articles/161668.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>内存泄漏</title><link>http://www.blogjava.net/sutao/articles/158763.html</link><dc:creator>苏醄</dc:creator><author>苏醄</author><pubDate>Wed, 07 Nov 2007 03:06:00 GMT</pubDate><guid>http://www.blogjava.net/sutao/articles/158763.html</guid><wfw:comment>http://www.blogjava.net/sutao/comments/158763.html</wfw:comment><comments>http://www.blogjava.net/sutao/articles/158763.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sutao/comments/commentRss/158763.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sutao/services/trackbacks/158763.html</trackback:ping><description><![CDATA[<h3><span style="font-size: 12pt;"><br />
</span>防范JAVA内存泄漏解决方案</h3>
<p style="text-align: left;" align="left">2007-04-12 12:41 &nbsp;&nbsp;&nbsp;&nbsp;<span style="font-family: 宋体;">推荐</span>:2129&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-family: 宋体;">收藏</span>:1369&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-family: 宋体;">评论</span>:0&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">来源</span>:<a href="http://www.it168.com/" target="_blank"><span style="color: black;">IT168</span></a><span style="font-size: 12pt; font-family: 宋体;"><br />
</span><span style="font-size: 12pt; font-family: 宋体;">编者按：Java内存泄漏是每个Java程序员都会遇到的问题，程序在本地运行一切正常，可是布署到远端就会出现内存无限制的增长，最后系统瘫痪，那么如何最快最好的检测程序的稳定性，防止系统崩盘，作者用自已的亲身经历与各位网友分享解决这些问题的办法。<span> <br />
<br />
</span>作为Internet最流行的编程语言之一,Java现正非常流行。我们的网络应用程序就主要采用Java语言开发，大体上分为客户端、服务器和数据库三 个层次。在进入测试过程中，我们发现有一个程序模块系统内存和CPU资源消耗急剧增加，持续增长到出现<span>
java.lang.OutOfMemoryError</span>为止。经过分析Java内存泄漏是破坏系统的主要因素。这里与大家分享我们在开发过程中遇到的 Java内存泄漏的检测和处理解决过程<span>. <br />
<br />
</span><strong>一. Java是如何管理内存</strong><span> <br />
<br />
</span>为了判断Java中是否有内存泄露，我们首先必须了解Java是如何管理内存的。Java的内存管理就是对象的分配和释放问题。在Java中，内存的分配 是由程序完成的，而内存的释放是由垃圾收集器(Garbage Collection，GC)完成的，程序员不需要通过调用函数来释放内存，但它只能回收无用并且不再被其它对象引用的那些对象所占用的空间。<span> <br />
<br />
Java</span>的内存垃圾回收机制是从程序的主要运行对象开始检查引用链，当遍历一遍后发现没有被引用的孤立对象就作为垃圾回收。GC为了能够正确释放对象，必 须监控每一个对象的运行状态，包括对象的申请、引用、被引用、赋值等，GC都需要进行监控。监视对象状态是为了更加准确地、及时地释放对象，而释放对象的 根本原则就是该对象不再被引用。<span> <br />
<br />
</span>在Java中，这些无用的对象都由GC负责回收，因此程序员不需要考虑这部分的内存泄露。虽然，我们有几个函数可以访问GC，例如运行GC的函数<span>
System.gc()</span>，但是根据Java语言规范定义，该函数不保证JVM的垃圾收集器一定会执行。因为不同的JVM实现者可能使用不同的算法管理 GC。通常GC的线程的优先级别较低。JVM调用GC的策略也有很多种，有的是内存使用到达一定程度时，GC才开始工作，也有定时执行的，有的是平缓执行 GC，有的是中断式执行GC。但通常来说，我们不需要关心这些。</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 12pt; font-family: 宋体;"><br />
<br />
</span><strong><span style="font-size: 12pt; font-family: 宋体;">一. 什么是Java中的内存泄露</span></strong><span style="font-size: 12pt; font-family: 宋体;"> <br />
<br />
</span><span style="font-size: 12pt; font-family: 宋体;">导致内存泄漏主要的原因是，先前申请了内存空间而忘记了释放。如果程序中存在对无用对象的引用，那么这些对象就会驻留内存，消耗内存，因为无法让垃圾回收
器GC验证这些对象是否不再需要。如果存在对象的引用，这个对象就被定义为"有效的活动"，同时不会被释放。要确定对象所占内存将被回收，我们就要务必确 认该对象不再会被使用。典型的做法就是把对象数据成员设为null或者从集合中移除该对象。但当局部变量不需要时，不需明显的设为null，因为一个方法
执行完毕时，这些引用会自动被清理。<span> <br />
<br />
</span>在Java中，内存泄漏就是存在一些被分配的对象，这些对象有下面两个特点，首先，这些对象是有被引用的，即在有向树形图中，存在树枝通路可以与其相连；
其次，这些对象是无用的，即程序以后不会再使用这些对象。如果对象满足这两个条件，这些对象就可以判定为Java中的内存泄漏，这些对象不会被GC所回 收，然而它却占用内存。<span> <br />
<br />
</span>这里引用一个常看到的例子，在下面的代码中，循环申请Object对象，并将所申请的对象放入一个Vector中，如果仅仅释放对象本身，但因为 Vector仍然引用该对象，所以这个对象对GC来说是不可回收的。因此，如果对象加入到Vector后，还必须从Vector中删除，最简单的方法就是 将Vector对象设置为null。</span></p>
<div style="border: 1pt solid black; padding: 3pt; background: #ededed none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;">
<p style="border: medium none ; padding: 0cm; background: #ededed none repeat scroll 0% 50%; text-align: left; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;" align="left"><span style="font-size: 12pt; font-family: 宋体;"><!--[if gte vml 1]>
<![endif]--><span style="color: black;">Vector v = </span><span style="color: blue;">new</span><span style="color: black;"> Vector(10);</span></span></p>
<p style="border: medium none ; padding: 0cm; background: #ededed none repeat scroll 0% 50%; text-align: left; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;" align="left"><span style="font-size: 12pt; font-family: 宋体; color: blue;">for</span><span style="font-size: 12pt; font-family: 宋体; color: black;"> (</span><span style="font-size: 12pt; font-family: 宋体; color: blue;">int</span><span style="font-size: 12pt; font-family: 宋体; color: black;"> i = 1; i &lt; 100; i++)</span></p>
<p style="border: medium none ; padding: 0cm; background: #ededed none repeat scroll 0% 50%; text-align: left; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;" align="left"><span style="font-size: 12pt; font-family: 宋体; color: black;">...</span><span style="font-size: 12pt; font-family: 宋体; color: black;">{</span></p>
<p style="border: medium none ; padding: 0cm; background: #ededed none repeat scroll 0% 50%; text-align: left; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;" align="left"><span style="font-size: 12pt; font-family: 宋体; color: black;">　Object o = </span><span style="font-size: 12pt; font-family: 宋体; color: blue;">new</span><span style="font-size: 12pt; font-family: 宋体; color: black;"> Object();</span></p>
<p style="border: medium none ; padding: 0cm; background: #ededed none repeat scroll 0% 50%; text-align: left; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;" align="left"><span style="font-size: 12pt; font-family: 宋体; color: black;">　v.add(o);</span></p>
<p style="border: medium none ; padding: 0cm; background: #ededed none repeat scroll 0% 50%; text-align: left; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;" align="left"><span style="font-size: 12pt; font-family: 宋体; color: black;">　o = </span><span style="font-size: 12pt; font-family: 宋体; color: blue;">null</span><span style="font-size: 12pt; font-family: 宋体; color: black;">;</span></p>
<p style="border: medium none ; padding: 0cm; background: #ededed none repeat scroll 0% 50%; text-align: left; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;" align="left"><span style="font-size: 12pt; font-family: 宋体; color: black;">}</span><span style="font-size: 12pt; font-family: 宋体; color: green;">//</span><span style="font-size: 12pt; font-family: 宋体; color: green;">此时，所有的Object对象都没有被释放，因为变量v引用这些对象。 </span></p>
</div>
<p style="text-align: left;" align="left"><span style="font-size: 12pt; font-family: 宋体;">实际上这些对象已经是无用的，但还被引用，GC就无能为力了(事实上GC认为它还有用)，这一点是导致内存泄漏最重要的原因。 再引用另一个例子来说明Java的内存泄漏。假设有一个日志类Logger，其提供一个静态的log(String msg)，任何其它类都可以调用Logger.Log(message)来将message的内容记录到系统的日志文件中。<span><br />
<br />
Logger</span>类有一个类型为HashMap的静态变量temp，每次在执行log(message)的时候，都首先将message的值写入temp中 (以当前线程+当前时间为键)，在退出之前再从temp中将以当前线程和当前时间为键的条目删除。注意，这里当前时间是不断变化的，所以log在退出之前 执行删除条目的操作并不能删除执行之初写入的条目。这样，任何一个作为参数传给log的字符串最终由于被Logger的静态变量temp引用，而无法得到 回收，这种对象保持就是我们所说的Java内存泄漏。
总的来说，内存管理中的内存泄漏产生的主要原因：保留下来却永远不再使用的对象引用。</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 12pt; font-family: 宋体;"><br />
<br />
</span><strong><span style="font-size: 12pt; font-family: 宋体;">三. 几种典型的内存泄漏</span></strong><span style="font-size: 12pt; font-family: 宋体;"> <br />
<br />
</span><span style="font-size: 12pt; font-family: 宋体;">我们知道了在Java中确实会存在内存泄漏，那么就让我们看一看几种典型的泄漏，并找出他们发生的原因和解决方法。<span> <br />
<br />
<strong>3.1 </strong></span><strong>全局集合</strong><span> <br />
</span>在大型应用程序中存在各种各样的全局数据仓库是很普遍的，比如一个JNDI-tree或者一个session table。在这些情况下，必须注意管理储存库的大小。必须有某种机制从储存库中移除不再需要的数据。<span> <br />
<br />
</span>通常有很多不同的解决形式，其中最常用的是一种周期运行的清除作业。这个作业会验证仓库中的数据然后清除一切不需要的数据。<span> <br />
<br />
</span>另一种管理储存库的方法是使用反向链接(referrer)计数。然后集合负责统计集合中每个入口的反向链接的数目。这要求反向链接告诉集合何时会退出入口。当反向链接数目为零时，该元素就可以从集合中移除了。<span> <br />
</span>　　<span> <br />
<strong>3.2 </strong></span><strong>缓存</strong><span> <br />
</span>缓存一种用来快速查找已经执行过的操作结果的数据结构。因此，如果一个操作执行需要比较多的资源并会多次被使用，通常做法是把常用的输入数据的操作结果进
行缓存，以便在下次调用该操作时使用缓存的数据。缓存通常都是以动态方式实现的,如果缓存设置不正确而大量使用缓存的话则会出现内存溢出的后果，因此需要
将所使用的内存容量与检索数据的速度加以平衡。<span> <br />
<br />
</span>常用的解决途径是使用java.lang.ref.SoftReference类坚持将对象放入缓存。这个方法可以保证当虚拟机用完内存或者需要更多堆的时候，可以释放这些对象的引用。<span> <br />
<br />
<strong>3.3 </strong></span><strong>类装载器</strong><span> <br />
Java</span>类装载器的使用为内存泄漏提供了许多可乘之机。一般来说类装载器都具有复杂结构，因为类装载器不仅仅是只与"常规"对象引用有关，同时也和对象内 部的引用有关。比如数据变量，方法和各种类。这意味着只要存在对数据变量，方法，各种类和对象的类装载器，那么类装载器将驻留在JVM中。既然类装载器可 以同很多的类关联，同时也可以和静态数据变量关联，那么相当多的内存就可能发生泄漏。</span></p>
<p style="text-align: left;" align="left"><span style="font-size: 12pt; font-family: 宋体;"><br />
</span><strong><strong><span style="font-size: 12pt; font-family: 宋体;">四. 如何检测和处理内存泄漏</span></strong><span style="font-size: 12pt; font-family: 宋体;"> <br />
</span><span style="font-size: 12pt; font-family: 宋体;">如何查找引起内存泄漏的原因一般有两个步骤:第一是安排有经验的编程人员对代码进行走查和分析，找出内存泄漏发生的位置;第二是使用专门的内存泄漏测试工具进行测试。<span> <br />
<br />
</span>第一个步骤在代码走查的工作中，可以安排对系统业务和开发语言工具比较熟悉的开发人员对应用的代码进行了交叉走查，尽量找出代码中存在的数据库连接声明和结果集未关闭、代码冗余等故障代码。<span> <br />
<br />
</span>第二个步骤就是检测Java的内存泄漏。在这里我们通常使用一些工具来检查Java程序的内存泄漏问题。市场上已有几种专业检查Java内存泄漏的工具，
它们的基本工作原理大同小异，都是通过监测Java程序运行时，所有对象的申请、释放等动作，将内存管理的所有信息进行统计、分析、可视化。开发人员将根
据这些信息判断程序是否有内存泄漏问题。这些工具包括Optimizeit Profiler，JProbe Profiler，JinSight , Rational 公司的Purify等。<span> <br />
<br />
<strong>4.1</strong></span><strong>检测内存泄漏的存在 </strong><span><br />
</span>这里我们将简单介绍我们在使用Optimizeit检查的过程。通常在知道发生内存泄漏之后，第一步是要弄清楚泄漏了什么数据和哪个类的对象引起了泄漏。<span> <br />
<br />
</span>一般说来，一个正常的系统在其运行稳定后其内存的占用量是基本稳定的，不应该是无限制的增长的。同样，对任何一个类的对象的使用个数也有一个相对稳定的上
限，不应该是持续增长的。根据这样的基本假设，我们持续地观察系统运行时使用的内存的大小和各实例的个数，如果内存的大小持续地增长，则说明系统存在内存 泄漏，如果特定类的实例对象个数随时间而增长（就是所谓的&#8220;增长率&#8221;），则说明这个类的实例可能存在泄漏情况。<span> <br />
<br />
</span>另一方面通常发生内存泄漏的第一个迹象是：在应用程序中出现了OutOfMemoryError。在这种情况下，需要使用一些开销较低的工具来监控和查找
内存泄漏。虽然OutOfMemoryError也有可能应用程序确实正在使用这么多的内存；对于这种情况则可以增加JVM可用的堆的数量，或者对应用程 序进行某种更改，使它使用较少的内存。<span> <br />
<br />
</span>但是，在许多情况下，OutOfMemoryError都是内存泄漏的信号。一种查明方法是不间断地监控GC的活动，确定内存使用量是否随着时间增加。如果确实如此，就可能发生了内存泄漏。</span></strong></p>
<p style="text-align: left;" align="left"><strong><strong><span style="font-size: 12pt; font-family: 宋体;"><br />
<br />
4.2</span></strong><strong><span style="font-size: 12pt; font-family: 宋体;">处理内存泄漏的方法</span></strong><span style="font-size: 12pt; font-family: 宋体;"> <br />
</span><span style="font-size: 12pt; font-family: 宋体;">一旦知道确实发生了内存泄漏，就需要更专业的工具来查明为什么会发生泄漏。JVM自己是不会告诉您的。这些专业工具从JVM获得内存系统信息的方法基本上 有两种：JVMTI和字节码技术(byte code instrumentation)。Java虚拟机工具接口(Java Virtual Machine Tools Interface，JVMTI)及其前身Java虚拟机监视程序接口<span>(Java Virtual Machine Profiling
Interface</span>，JVMPI)是外部工具与JVM通信并从JVM收集信息的标准化接口。字节码技术是指使用探测器处理字节码以获得工具所需的信息的技 术。<span> <br />
<br />
Optimizeit</span>是Borland公司的产品，主要用于协助对软件系统进行代码优化和故障诊断，其中的Optimizeit Profiler主要用于内存泄漏的分析。Profiler的堆视图就是用来观察系统运行使用的内存大小和各个类的实例分配的个数的。<span> <br />
<br />
</span>首先，Profiler会进行趋势分析，找出是哪个类的对象在泄漏。系统运行长时间后可以得到四个内存快照。对这四个内存快照进行综合分析，如果每一次快
照的内存使用都比上一次有增长，可以认定系统存在内存泄漏，找出在四个快照中实例个数都保持增长的类，这些类可以初步被认定为存在泄漏。通过数据收集和初 步分析，可以得出初步结论:系统是否存在内存泄漏和哪些对象存在泄漏(被泄漏)。<span> <br />
<br />
</span>接下来，看看有哪些其他的类与泄漏的类的对象相关联。前面已经谈到Java中的内存泄漏就是无用的对象保持，简单地说就是因为编码的错误导致了一条本来不
应该存在的引用链的存在(从而导致了被引用的对象无法释放)，因此内存泄漏分析的任务就是找出这条多余的引用链，并找到其形成的原因。查看对象分配到哪里
是很有用的。同时只知道它们如何与其他对象相关联（即哪些对象引用了它们）是不够的，关于它们在何处创建的信息也很有用。<span> <br />
<br />
</span>最后，进一步研究单个对象，看看它们是如何互相关联的。借助于Profiler工具，应用程序中的代码可以在分配时进行动态添加，以创建堆栈跟踪。也有可
以对系统中所有对象分配进行动态的堆栈跟踪。这些堆栈跟踪可以在工具中进行累积和分析。对每个被泄漏的实例对象，必然存在一条从某个牵引对象出发到达该对 象的引用链。处于堆栈空间的牵引对象在被从栈中弹出后就失去其牵引的能力，变为非牵引对象。因此，在长时间的运行后，被泄露的对象基本上都是被作为类的静
态变量的牵引对象牵引。<span> <br />
</span>总而言之, Java虽然有自动回收管理内存的功能,但内存泄漏也是不容忽视,它往往是破坏系统稳定性的重要因素。</span></strong></p>
<img src ="http://www.blogjava.net/sutao/aggbug/158763.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sutao/" target="_blank">苏醄</a> 2007-11-07 11:06 <a href="http://www.blogjava.net/sutao/articles/158763.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>InputStream 和OutputStream</title><link>http://www.blogjava.net/sutao/articles/141053.html</link><dc:creator>苏醄</dc:creator><author>苏醄</author><pubDate>Wed, 29 Aug 2007 10:52:00 GMT</pubDate><guid>http://www.blogjava.net/sutao/articles/141053.html</guid><wfw:comment>http://www.blogjava.net/sutao/comments/141053.html</wfw:comment><comments>http://www.blogjava.net/sutao/articles/141053.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sutao/comments/commentRss/141053.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sutao/services/trackbacks/141053.html</trackback:ping><description><![CDATA[<p><strong> <font color="#0000ff">4．4．2 InputStream 和OutputStream</font></strong>
</p>
<p>
</p>
<p>　1．InputStream<br>
<br>
◇ 从流中读取数据：<br>
int read( ); //读取一个字节，返回值为所读的字节<br>
int read( byte b[ ] ); <font color="#339900"> //读取多个字节，放置到字节数组b中，通常<br>
//读取的字节数量为b的长度，返回值为实际<br>
//读取的字节的数量</font><br>
int read( byte b[ ], int off, int len ); <font color="#339900"> //读取len个字节，放置<br>
//到以下标off开始字节<br>
//数组b中，返回值为实<br>
//际读取的字节的数量</font><br>
int available( ); 　　<font color="#339900">//返回值为流中尚未读取的字节的数量</font><br>
long skip( long n )； <font color="#339900"> //读指针跳过n个字节不读，返回值为实际<br>
//跳过的字节数量</font><br>
<br>
◇ 关闭流：<br>
close( ); <font color="#339900"> //流操作完毕后必须关闭</font><br>
<br>
◇ 使用输入流中的标记：<br>
void mark( int readlimit ); <font color="#339900"> //记录当前读指针所在位置，readlimit<br>
//表示读指针读出</font><font color="#339900">readlimit个字节后<br>
//所标记的指针位置才失效</font><br>
void reset( ); 　　　 <font color="#339900"> //把读指针重新指向用mark方法所记录的位置</font><br>
boolean markSupported( );　<font color="#339900">//当前的流是否支持读指针的记录功能</font><br>
<br>
<font color="#000099">有关每个方法的使用，详见java API。</font><br>
<br>
<br>
2．OutputStream<br>
<br>
◇ 输出数据：<br>
void write( int b ); 　　<font color="#339900">//往流中写一个字节b</font><br>
void write( byte b[ ] ); <font color="#339900"> //往流中写一个字节数组b</font><br>
void write( byte b[ ], int off, int len ); <font color="#339900">//把字节数组b中从<br>
//下标off开始，长度为len的字节写入流中</font><br>
<br>
◇ flush( ) 　　　　　　<font color="#339900">//刷空输出流，并输出所有被缓存的字节</font><br>
由于某些流支持缓存功能，该方法将把缓存中所有内容强制输出到流中。<br>
<br>
◇ 关闭流：<br>
close( ); <font color="#339900"> 　　　　　　//流操作完毕后必须关闭</font>
</p>
<p>
</p>
<p><strong> <font color="#0000ff">4．4．3 I/O中的例外</font></strong>
</p>
<p>
</p>
<p>　　进行I/O操作时可能会产生I/O例外，属于非运行时例外，应该在程序中处理。如：FileNotFoundException, EOFException,
IOException
</p>
<p>
</p>
<p><font size="2"><font color="#ff0000"><strong> 4．5 文件处理</strong> <br>
</font><br>
</font>
I/O处理中，最常见的是对文件的操作，java.io包中有关文件处理的类有：File、FileInputStream、
FileOutputStream、RamdomAccessFile和FileDescriptor；接口有：FilenameFilter。<br>
<br>
<strong> <font color="#0000ff">4．5．1 文件描述</font></strong> <br>
<br>
类File提供了一种与机器无关的方式来描述一个文件对象的属性。下面我们介绍类File中提供的各种方法。
</p>
<p>
</p>
<p>　◇ 文件或目录的生成<br>
<br>
public File(String path)；<font color="#339900">/*如果path是实际存在的路径，则该File对象<br>
/*表示的是目录；如果path是文件名，则该File对象表示的是文件。*/</font><br>
public File(String path,String name)；<font color="#339900">//path是路径名，name是文件名</font><br>
public File(File dir,String name)；<font color="#339900">//dir是路径名，name是文件名</font><br>
<br>
◇ 文件名的处理<br>
<br>
String getName( )； <font color="#339900"> //得到一个文件的名称（不包括路径）</font><br>
String getPath( )； <font color="#339900"> //得到一个文件的路径名</font><br>
String getAbsolutePath( )；<font color="#339900">//得到一个文件的绝对路径名</font><br>
String getParent( )； <font color="#339900"> //得到一个文件的上一级目录名</font><br>
String renameTo(File newName); <font color="#339900"> //将当前文件名更名为给定文件的<br>
完整路径<br>
</font><br>
◇ 文件属性测试<br>
<br>
boolean exists( )； <font color="#339900"> //测试当前File对象所指示的文件是否存在</font><br>
boolean canWrite( )；<font color="#339900">//测试当前文件是否可写</font><br>
boolean canRead( )；<font color="#339900">//测试当前文件是否可读</font><br>
boolean isFile( )； <font color="#339900"> //测试当前文件是否是文件（不是目录）</font><br>
boolean isDirectory( )； <font color="#339900"> //测试当前文件是否是目录<br>
</font><br>
◇ 普通文件信息和工具<br>
<br>
long lastModified( );<font color="#339900">//得到文件最近一次修改的时间</font><br>
long length( ); <font color="#339900"> //得到文件的长度，以字节为单位</font><br>
boolean delete( ); <font color="#339900"> //删除当前文件</font><br>
<br>
◇ 目录操作<br>
<br>
boolean mkdir( ); <font color="#339900"> //根据当前对象生成一个由该对象指定的路径</font><br>
String list( ); <font color="#339900"> //列出当前目录下的文件<br>
</font><br>
<strong>【例4-3】</strong><br>
import java.io.*; <font color="#339900"> //引入java.io包中所有的类</font><br>
public class FileFilterTest{<br>
public static void main(String args[]){<br>
File dir=new File("d://ex"); <font color="#339900"> //用File 对象表示一个目录</font><br>
Filter filter=new Filter("java"); <font color="#339900"> //生成一个名为java的过滤器</font><br>
System.out.println("list java files in directory "+dir);<br>
String files[]=dir.list(filter); <font color="#339900"> //列出目录dir下，文件后缀名<br>
为java的所有文件</font><br>
for(int i=0;i&lt;files.length;i++){<br>
File f=new File(dir,files[i]); <font color="#339900"> //为目录dir 下的文件或目录<br>
创建一个File 对象</font><br>
if(f.isFile()) <font color="#339900"> //如果该对象为后缀为java的文件，<br>
则打印文件名</font><br>
System.out.println("file "+f);<br>
else<br>
System.out.println("sub directory "+f ); <font color="#339900">//如果是目录<br>
则打印目录名</font><br>
}<br>
}<br>
}<br>
class Filter implements FilenameFilter{<br>
String extent;<br>
Filter(String extent){<br>
this.extent=extent;<br>
}<br>
public boolean accept(File dir,String name){<br>
return name.endsWith("."+extent); <font color="#339900"> //返回文件的后缀名</font><br>
}<br>
}
</p>
<p>
</p>
<p>&nbsp;
</p>
<p>
</p>
<p>&nbsp;
</p>
<p>
</p>
<p>其他内容请看：
</p>
<p>
</p>
<p><a href="http://www.bc-cn.net/Article/kfyy/java/jc/200410/81_4.html"> http://www.bc-cn.net/Article/kfyy/java/jc/200410/81_4.html</a></p>
<p> 补充日期: <strong> 2005-03-15
10:31:37</strong>
</p>
<p>
<table border="0" cellpadding="0" cellspacing="0" width="580">
    <tbody>
        <tr>
            <td>
            <p><strong> <font color="#0000ff">4．5．2 文件的顺序处理<br>
            </font></strong> <br>
            类FileInputStream和FileOutputStream用来进行文件I/O处理，由它们所提供的方法可以打开本地主机上的文件，并进行
            顺序的读/写。例如，下列的语句段是顺序读取文件名为text的文件里的内容，并显示在控制台上面，直到文件结束为止。 </p>
            <p>
            </p>
            <p>　　FileInputStream fis;<br>
            try{<br>
            fis = new FileInputStream( "text" );<br>
            System.out.print( "content of text is : ");<br>
            int b;<br>
            while( (b=fis.read())!=-1 ) <font color="#339900"> //顺序读取文件text里的内容并赋值<br>
            给整型变量b,</font><font color="#339900">直到文件结束为止。</font><br>
            { 　　　　　　　　　　　　　<br>
            System.out.print( (char)b );<br>
            }<br>
            }catch( FileNotFoundException e ){<br>
            System.out.println( e );<br>
            }catch( IOException e ){<br>
            System.out.println( e );<br>
            }
            </p>
            <p>
            </p>
            <p><strong> <font color="#0033ff">4．5．3 随机访问文件</font></strong> <br>
            <br>
            对于InputStream 和OutputStream 来说，它们的实例都是顺序访问流，也就是说，只能对文件进行顺序地读/写。随机访问文件则允许对文件内容进行随机读/写。在java中，类RandomAccessFile
            提供了随机访问文件的方法。类RandomAccessFile的声明为：<br>
            public class RandomAccessFile extends Object implements DataInput, DataOutput
            </p>
            <p>
            </p>
            <p>　　接口DataInput 中定义的方法主要包括从流中读取基本类型的数据、读取一行数据、或者读取指定长度的字节数。如：readBoolean(
            )、readInt( )、readLine( )、readFully( ) 等。<br>
            <br>
            接口DataOutput 中定义的方法主要是向流中写入基本类型的数据、或者写入一定长度的字节数组。如：writeChar( )、writeDouble(
            )、write( ) 等。 下面详细介绍RandomAccessFile类中的方法。<br>
            <br>
            <strong>◇ 构造方法：</strong><br>
            RandomAccessFile(String name,String mode); <font color="#339900">//name是文件名，mode<br>
            //是打开方式，例如"r"表示只读，"rw"表示可读写，"</font><br>
            RandomAccessFile(File file,String mode); <font color="#339900"> //file是文件对象</font><br>
            <br>
            <strong>◇ 文件指针的操作</strong><br>
            long getFilePointer( ); <font color="#339900"> //用于得到当前的文件指针</font><br>
            void seek( long pos ); <font color="#339900"> //用于移动文件指针到指定的位置</font><br>
            int skipBytes( int n ); <font color="#339900"> //使文件指针向前移动指定的n个字节</font>
            </p>
            <p>
            </p>
            <p><strong> <font color="#ff0000" size="2">4．6 过滤流</font></strong> <br>
            <br>
            过滤流在读/写数据的同时可以对数据进行处理，它提供了同步机制，使得某一时刻只有一个线程可以访问一个I/O流，以防止多个线程同时对一个I/O流进行
            操作所带来的意想不到的结果。类FilterInputStream和FilterOutputStream分别作为所有过滤输入流和输出流的父类 </p>
            <p>
            </p>
            <p><font color="#000099">　过滤流类层次：<br>
            </font>
            </p>
            <p>
            </p>
            <p><strong> <font size="3">　　　　　　&nbsp; java.lang.Object<br>
            &nbsp; |<br>
            &nbsp; +----java.io.InputStream<br>
            &nbsp; |<br>
            &nbsp; +----java.io.FilterInputStream</font></strong>
            </p>
            <p>
            </p>
            <p><br>
            <br>
            为了使用一个过滤流，必须首先把过滤流连接到某个输入/出流上，通常通过在构造方法的参数中指定所要连接的输入/出流来实现。例如：<br>
            <br>
            FilterInputStream( InputStream in );<br>
            FilterOutputStream( OutputStream out );
            </p>
            <p>
            </p>
            <p><strong> <font color="#0000ff">4．6．1 几种常见的过滤流</font></strong>
            </p>
            <p>
            </p>
            <p>　　<strong>◇ BufferedInputStream和BufferedOutputStream</strong><br>
            缓冲流，用于提高输入/输出处理的效率。<br>
            <br>
            <strong>◇ DataInputStream 和 DataOutputStream</strong><br>
            不仅能读/写数据流，而且能读/写各种的java语言的基本类型，如：boolean，int，float等。<br>
            <br>
            <strong>◇ LineNumberInputStream</strong><br>
            除了提供对输入处理的支持外，LineNumberInputStream可以记录当前的行号。<br>
            <br>
            <strong>◇ PushbackInputStream</strong><br>
            提供了一个方法可以把刚读过的字节退回到输入流中，以便重新再读一遍。<br>
            <br>
            <strong>◇ PrintStream</strong><br>
            打印流的作用是把Java语言的内构类型以其字符表示形式送到相应的输出流。
            </p>
            <p>
            </p>
            <p><strong> <font color="#ff0000" size="2">4．7 字符流的处理</font></strong> <br>
            <br>
            java中提供了处理以16位的Unicode码表示的字符流的类，即以Reader和Writer 为基类派生出的一系列类。<br>
            <br>
            <strong> <font color="#0000ff">4．7．1 Reader和Writer</font></strong>
            </p>
            <p>
            </p>
            <p>　&nbsp; 这两个类是抽象类，只是提供了一系列用于字符流处理的接口，不能生成这两个类的实例，只能通过使用由它们派生出来的子类对象来处理字符流。<br>
            <br>
            1．Reader类是处理所有字符流输入类的父类。<br>
            <br>
            <strong>◇ 读取字符</strong><br>
            public int read() throws IOException; //读取一个字符，返回值为读取的字符<br>
            public int read(char cbuf[]) throws IOException; /*读取一系列字符到数组cbuf[]中，返回值为实际读取的字符的数量*/<br>
            public abstract int read(char cbuf[],int off,int len) throws IOException;<br>
            /*读取len个字符，从数组cbuf[]的下标off处开始存放，返回值为实际读取的字符数量，该方法必须由子类实现*/<br>
            <br>
            <strong>◇ 标记流</strong><br>
            public boolean markSupported(); <font color="#339900"> //判断当前流是否支持做标记</font><br>
            public void mark(int readAheadLimit) throws IOException;<br>
            <font color="#339900"> //给当前流作标记，最多支持readAheadLimit个字符的回溯。</font><br>
            public void reset() throws IOException; <font color="#339900"> //将当前流重置到做标记处</font><br>
            <br>
            <strong>◇ 关闭流</strong><br>
            public abstract void close() throws IOException;<br>
            <br>
            2． Writer类是处理所有字符流输出类的父类。<br>
            <br>
            <strong>◇ 向输出流写入字符</strong><br>
            public void write(int c) throws IOException；<font color="#339900"><br>
            //将整型值c的低16位写入输出流</font><br>
            public void write(char cbuf[]) throws IOException；<font color="#339900"><br>
            //将字符数组cbuf[]写入输出流</font><br>
            public abstract void write(char cbuf[],int off,int len) throws IOException；<br>
            <font color="#339900">//将字符数组cbuf[]中的从索引为off的位置处开始的len个字符写入输出流</font><br>
            public void write(String str) throws IOException；<br>
            <font color="#339900">//将字符串str中的字符写入输出流</font><br>
            public void write(String str,int off,int len) throws IOException；<br>
            <font color="#339900">//将字符串str 中从索引off开始处的len个字符写入输出流</font><br>
            <br>
            <strong>◇ flush( )</strong><br>
            刷空输出流，并输出所有被缓存的字节。<br>
            <br>
            <strong>◇ 关闭流</strong><br>
            public abstract void close() throws IOException；</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p> 补充日期: <strong> 2005-03-15
10:39:31</strong>
</p>
<p>
<table border="0" cellpadding="0" cellspacing="0" width="580">
    <tbody>
        <tr>
            <td>
            <p>&nbsp;
            </p>
            <p>关于 java IO 流 ；这个也不错，只是拷贝不下来。
            </p>
            <p><a href="http://edu.cn700.com/Print.asp?ArticleID=18540"> http://edu.cn700.com/Print.asp?ArticleID=18540</a></p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p> 补充日期: <strong> 2005-03-15
10:45:25</strong>
</p>
<p>
<table border="0" cellpadding="0" cellspacing="0" width="580">
    <tbody>
        <tr>
            <td><strong> <font color="#0000ff">4．7．2 InputStreamReader和OutputStreamWriter</font></strong> <br>
            <br>
            java.io包中用于处理字符流的最基本的类,用来在字节流和字符流之间作为中介。
            <p><strong> 　 ◇ 生成流对象<br>
            </strong> 　　public InputStreamReader(InputStream in); <font color="#339900"><br>
            /*in是字节流，而InputStreamReader是字符流，但是其来源是字节流in，<br>
            因此InputStreamReader就可以把字节流in转换成字符流处理。/*</font><br>
            <br>
            public InputStreamReader(InputStream in,String enc) throws UnsupportedEncodingException;<br>
            <font color="#339900">/*enc是编码方式，就是从字节流到字符流进行转换时所采用的编码方式，<br>
            例如 ISO8859-1，UTF-8，UTF-16等等*/</font><br>
            <br>
            public OutputStreamWriter(OutputStream out);<br>
            <font color="#339900">/*out是字节流，而OutputStreamReader是字符流 */</font><br>
            <br>
            public OutputStreamWriter(OutputStream out,String enc) throws UnsupportedEncodingException;
            <font color="#339900"> //enc是编码方式</font><br>
            <br>
            InputStreamReader和OutputStreamWriter的方法：<br>
            <br>
            <strong>◇ 读入和写出字符</strong><br>
            基本同Reader和Writer。<br>
            <br>
            <strong>◇ 获取当前编码方式</strong><br>
            public String getEncoding();<br>
            <br>
            <strong>◇ 关闭流</strong><br>
            public void close() throws IOException;
            </p>
            <p>
            </p>
            <p><strong> <font color="#0033ff">4．7．3 BufferedReader和BufferedWriter</font></strong>
            </p>
            <p>
            </p>
            <p>　◇ 生成流对象<br>
            <br>
            public BufferedReader(Reader in); <font color="#339900"> //使用缺省的缓冲区大小</font><br>
            public BufferedReader(Reader in, int sz); <font color="#339900">//sz为缓冲区的大小</font><br>
            public BufferedWriter(Writer out);<br>
            public BufferedWriter(Writer out, int sz);<br>
            <br>
            ◇ 读入/写出字符<br>
            <br>
            除了Reader和Writer中提供的基本的读写方法外，增加对整行字符的处理。<br>
            public String readLine() throws IOException; <font color="#339900">//读一行字符</font><br>
            public void newLine() throws IOException; <font color="#339900">//写一行字符<br>
            </font><strong><br>
            【例4-4】</strong><br>
            <br>
            import java.io.*;<br>
            public class NumberInput{<br>
            public static void main(String args[]){<br>
            try{<br>
            InputStreamReader ir;<br>
            BufferedReader in;<br>
            ir=new InputStreamReader(System.in);<br>
            <font color="#339900">//从键盘接收了一个字符串的输入，并创建了一个字符输入流的对象</font><br>
            in=new BufferedReader(ir);<br>
            String s=in.readLine();<br>
            <font color="#339900">//从输入流in中读入一行，并将读取的值赋值给字符串变量s</font><br>
            System.out.println("Input value is: "+s);<font color="#339900"><br>
            </font>int i = Integer.parseInt(s);<font color="#339900">//转换成int型</font><br>
            i*=2;<br>
            System.out.println("Input value changed after doubled: "+i);<br>
            }catch(IOException e)<br>
            {System.out.println(e);}<br>
            }<br>
            }<br>
            <br>
            <font color="#ff0000">运行结果</font>
            </p>
            <p>
            </p>
            <p>
            <table bgcolor="#ffffff" border="2" bordercolor="#cccccc" cellpadding="2" cellspacing="0">
                <tbody>
                    <tr>
                        <td>D:\&gt;java NumberInput<br>
                        123<br>
                        Input value is 123<br>
                        Input value changed after doubled： 246</td>
                    </tr>
                </tbody>
            </table>
            <br>
            <br>
            </p>
            <p>
            </p>
            <p><br>
            <br>
            <font color="#000099">注意：在读取字符流时，如果不是来自于本地的，比如说来自于网络上某处的与本地编码方式不同的机器，那么我们在构造输入流时就不能简单地使用本地缺省的编码方式，否则读出的字符就不正确；为了正确地读出异种机上的字符，我们应该使用下述方式构造输入流对象：<br>
            <br>
            ir = new InputStreamReader(is, "8859_1");<br>
            <br>
            采用ISO 8859_1编码方式，这是一种映射到ASCII码的编码方式，可以在不同平台之间正确转换字符。</font></p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p> 补充日期: <strong> 2005-03-15
10:56:05</strong>
</p>
<p>
<table border="0" cellpadding="0" cellspacing="0" width="580">
    <tbody>
        <tr>
            <td>
            <p><strong> <font color="#ff0000" size="2">4．8 对象的串行化(Serialization)</font></strong>
            </p>
            <p>
            </p>
            <p><strong> <font color="#0000ff">4．8．1 串行化的定义</font></strong>
            </p>
            <p>
            </p>
            <p>　　1． 什么是串行化<br>
            <br>
            对象的寿命通常随着生成该对象的程序的终止而终止。有时候，可能需要将对象的状态保存下来，在需要时再将对象恢复。我们把对象的这种能记录自己的状态
            以便将来再生的能力，叫做对象的持续性(persistence)。对象通过写出描述自己状态的数值来记录自己，这个过程叫对象的串行化
            (Serialization)。 </p>
            <p>
            </p>
            <p>　　2． 串行化的目的<br>
            <br>
            串行化的目的是为java的运行环境提供一组特性，其主要任务是写出对象实例变量的数值。
            </p>
            <p>
            </p>
            <p><strong> <font color="#0000ff">4．8．2 串行化方法</font></strong> <br>
            <br>
            在java.io包中，接口Serializable用来作为实现对象串行化的工具，只有实现了Serializable的类的对象才可以被串行化。
            </p>
            <p>
            </p>
            <p>　1． 定义一个可串行化对象<br>
            <br>
            public class Student implements Serializable{<br>
            int id; <font color="#339900">//学号</font><br>
            String name; <font color="#339900">//姓名</font><br>
            int age; <font color="#339900">//年龄</font><br>
            String department <font color="#339900">//系别</font><br>
            public Student(int id,String name,int age,String department){<br>
            this.id = id;<br>
            this.name = name;<br>
            this.age = age;<br>
            this.department = department;<br>
            }<br>
            }<br>
            <br>
            2． 构造对象的输入/输出流<br>
            <br>
            要串行化一个对象，必须与一定的对象输入/输出流联系起来，通过对象输出流将对象状态保存下来，再通过对象输入流将对象状态恢复。<br>
            <br>
            java.io包中，提供了ObjectInputStream和ObjectOutputStream将数据流功能扩展至可读写对象。在
            ObjectInputStream中用readObject()方法可以直接读取一个对象，ObjectOutputStream中用
            writeObject()方法可以直接将对象保存到输出流中。<br>
            <br>
            Student stu=new Student(981036,"Liu Ming",18, "CSD");<br>
            FileOutputStream fo=new FileOutputStream("data.ser");<br>
            <font color="#339900">//保存对象的状态</font><br>
            ObjectOutputStream so=new ObjectOutputStream(fo);<br>
            try{<br>
            so.writeObject(stu);<br>
            so.close();<br>
            }catch(IOException e )<br>
            {System.out.println(e);}<br>
            FileInputStream fi=new FileInputStream("data.ser");<br>
            ObjectInputStream si=new ObjectInputStream(fi);<br>
            <font color="#339900">//恢复对象的状态</font><br>
            try{<br>
            stu=(Student)si.readObject();<br>
            si.close();<br>
            }catch(IOException e )<br>
            {System.out.println(e);}<br>
            <br>
            <font color="#000099">在
            这个例子中，我们首先定义一个类Student，实现了
            Serializable接口，然后通过对象输出流的writeObject()方法将Student对象保存到文件data.ser中。之后，通过对象
            输入流的readObject()方法从文件data.ser中读出保存下来的Student对象。</font>
            </p>
            <p>
            </p>
            <p><strong> <font color="#0000ff">4．8．3 串行化的注意事项</font></strong>
            </p>
            <p>
            </p>
            <p>　1．串行化能保存的元素<br>
            <br>
            只能保存对象的非静态成员变量，不能保存任何的成员方法和静态的成员变量，而且串行化保存的只是变量的值，对于变量的任何修饰符，都不能保存。<br>
            <br>
            2．transient关键字<br>
            <br>
            对于某些类型的对象，其状态是瞬时的，这样的对象是无法保存其状态的，例如一个Thread对象，或一个FileInputStream对象，对于这些字段，我们必须用transient关键字标明<br>
            <br>
            3． 定制串行化<br>
            <br>
            缺省的串行化机制，对象串行化首先写入类数据和类字段的信息，然后按照名称的上升排列顺序写入其数值。如果想自己明确地控制这些数值的写入顺序和写入
            种类，必须定义自己的读取数据流的方式。就是在类的定义中重写writeObject()和readObject()方法。<br>
            <br>
            例如可在4.8.2的例子中，加入重写的writeObject()和readObject()方法，对Student 类定制其串行化。<br>
            <br>
            private void writeObject(ObjectOutputStream out)throws IOException<br>
            {<br>
            out.writeInt(id);<br>
            out.writeInt(age);<br>
            out.writeUTF(name);<br>
            out.writeUTF(department);<br>
            }<br>
            private void readObject(ObjectInputStream in)throws IOException<br>
            {<br>
            id=in.readInt();<br>
            age=in.readInt();<br>
            name=in.readUTF();<br>
            department=in.readUTF();<br>
            }</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p> 补充日期: <strong> 2005-03-15
11:14:20</strong>
</p>
<p>
<table border="0" cellpadding="0" cellspacing="0" width="580">
    <tbody>
        <tr>
            <td>
            <p><strong> <font color="#ff0000" size="2">4．9 其它常用的流</font></strong> <br>
            <br>
            <strong> <font color="#0000ff">4．9．1 管道流</font></strong>
            </p>
            <p>
            </p>
            <p>　　管道用来把一个程序、线程或代码块的输出连接到另一个程序、线程或代码块的输入 。<br>
            <br>
            管道输入流作为一个通信管道的接收端，管道输出流作为发送端。在使用管道之前，管道输出流和管道输入流必须进行连接。下面有两种连接的方法：<br>
            <br>
            1． 构造方法中连接<br>
            <br>
            PipedInputStream(PipedOutputStream src);<br>
            PipedOutputStream(PipedInputStream snk);<br>
            <br>
            2． connect()方法进行连接<br>
            <br>
            类PipedInputStream中定义为：<br>
            void connect(PipedOutputStream src);<br>
            类PipedOutputStream中定义为：<br>
            void connect(PipedInputStream snk);
            </p>
            <p>
            </p>
            <p><strong> <font color="#0000ff">4．9．2 内存的读/写</font></strong>
            </p>
            <p>
            </p>
            <p>　1． ByteArrayInputStream和ByteArrayOutputStream<br>
            <br>
            ByteArrayInputStream <font color="#339900"> //从字节数组中读取以字节为单位的数据</font><br>
            ByteArrayOutputStream <font color="#339900"> //向字节数组中写入以字节为单位的数据</font><br>
            <br>
            2． StringBufferInputStream和StringBufferOutputStream<br>
            <br>
            StringBufferInputStream <font color="#339900"><br>
            //从字符串缓冲区StringBuffer中读取以字符为单位的数据</font><br>
            StringBufferOutputStream <font color="#339900"><br>
            //向字符串缓冲区StringBuffer中写入以字符为单位的数据</font>
            </p>
            <p>
            </p>
            <p><strong> <font color="#0000ff">4．9．3 顺序输入流</font></strong>
            </p>
            <p>
            </p>
            <p>　　SequenceInputStream 把几个输入流顺序连接起来。顺序输入流提供了把若干不同的流统一为同一个流的功能，使得程序变得更加简洁。
            </p>
            <p>
            </p>
            <p><strong> <font color="#ff0000" size="2"> 【本讲小结】</font></strong>
            </p>
            <p>　　例外处理是java语言中一个独特之处，主要使用捕获例外和声明抛弃例外两种方法来处理程序中可能出现例外的语句块，其中捕获例外的方法是一种积极地处理例外的方法，而声明抛弃例外是一种消极的处理例外的方法。<br>
            <br>
            Java中的输入／输出处理是通过使用流技术，用统一的接口表示而实现的。输入／输出流中，最常见的是对文件的处理。Java语言中提供专门处理文件
            和目录的类，例如：java.io.File，java.io.FileInputStream，java.io.FileOutputStream，
            java.io.RandomAccessFile和接口java.io.FilenameFilter。输入／输出流根据处理的内容，分为字符流和字节
            流两种，其中字节流是以byte为基本处理单位的流；而字符流是以16位的Unicode码为处理单位的流。</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p> 补充日期: <strong> 2005-03-30
09:56:05</strong>
</p>
<p>java文件操作,再看
</p>
<p><a href="http://www.tiantiansoft.com/Article_Show.asp?ArticleID=163">http://www.tiantiansoft.com/Article_Show.asp?ArticleID=163</a></p>
<br><img src ="http://www.blogjava.net/sutao/aggbug/141053.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sutao/" target="_blank">苏醄</a> 2007-08-29 18:52 <a href="http://www.blogjava.net/sutao/articles/141053.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA-IO详解 </title><link>http://www.blogjava.net/sutao/articles/141051.html</link><dc:creator>苏醄</dc:creator><author>苏醄</author><pubDate>Wed, 29 Aug 2007 10:47:00 GMT</pubDate><guid>http://www.blogjava.net/sutao/articles/141051.html</guid><wfw:comment>http://www.blogjava.net/sutao/comments/141051.html</wfw:comment><comments>http://www.blogjava.net/sutao/articles/141051.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sutao/comments/commentRss/141051.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sutao/services/trackbacks/141051.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: JAVA-IO详解&nbsp; 一．&nbsp;Input和Output &nbsp;&nbsp;1.&nbsp;stream代表的是任何有能力产出数据的数据源，或是任何有能力接收数据的接收源。在Java的IO中，所有的stream（包括Input和Out&nbsp;stream）都包括两种类型： &nbsp;1.1&nbsp;以字节为导向的stream &nbsp;&nbsp;以字节为导向的st...&nbsp;&nbsp;<a href='http://www.blogjava.net/sutao/articles/141051.html'>阅读全文</a><img src ="http://www.blogjava.net/sutao/aggbug/141051.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sutao/" target="_blank">苏醄</a> 2007-08-29 18:47 <a href="http://www.blogjava.net/sutao/articles/141051.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>研究 Java 中 XML 文档模型的特性和性能</title><link>http://www.blogjava.net/sutao/articles/141000.html</link><dc:creator>苏醄</dc:creator><author>苏醄</author><pubDate>Wed, 29 Aug 2007 09:17:00 GMT</pubDate><guid>http://www.blogjava.net/sutao/articles/141000.html</guid><wfw:comment>http://www.blogjava.net/sutao/comments/141000.html</wfw:comment><comments>http://www.blogjava.net/sutao/articles/141000.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sutao/comments/commentRss/141000.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sutao/services/trackbacks/141000.html</trackback:ping><description><![CDATA[<span style="color: #1826ff;">研究 Java 中 XML 文档模型的特性和性能</span><br style="color: #1826ff;">
<p style="color: #1826ff;"><a href="http://www-128.ibm.com/developerworks/cn/xml/x-injava/index.html#author1"><name>Dennis M. Sosnoski</name></a><br>总裁, Sosnoski Software Solutions, Inc.<br>2001 年 9 月 </p>
<blockquote style="color: #1826ff;"><abstract-extended>在本文中，Java 顾问 Dennis Sosnoski 比较几个 Java
文档模型的性能和功能。当选择模型时，无法做到每次都权衡得很清楚，如果以后改变主意，则需要大量编码来进行切换。作者将性能结果放入特性集合的上下文中
并遵循标准，对所要求的正确选择给出了一些建议。本文包含用于这组测试的几张图表和源代码。</abstract-extended></blockquote>
<p style="color: #1826ff;">使用内存中 XML 文档的 Java 开发者可以选择使用标准 DOM 表示或几个 Java 特定模型中的任何一个。该灵活性已经帮助将 Java 建立成 XML 工作的出色平台。但是，由于不同模型数量的增加，已经更加难以确定如何比较模型的功能、性能和易用性。</p>
<p style="color: #1826ff;">关于使用&#8220;Java 中的 XML&#8221;系列中的第一篇文章研究了 Java 中一些领先的 XML 文档模型的特性和性能。它包括一组性能测试的结果(带有可下载的测试代码，请参阅 <a href="http://www-128.ibm.com/developerworks/cn/xml/x-injava/index.html#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml">参考资料</a>）。在系列中的第二篇文章将通过比较为实现同样任务所使用的不同模型的样本代码来研究易用性问题。 </p>
<p style="color: #1826ff;"><a name="N1005E">文档模型</a><br>Java 中的可用文档模型数一直在增加。对于本文，我已经涵盖了最常用的模型和几项选择，这演示了那些可能还未被广泛了解或使用的特别令人感兴趣的特性。随着&#8220;XML 名称空间&#8221;的重要性增加，我已经包含了仅支持该功能的模型。下面列出了带有简要介绍和版本信息的模型。</p>
<p style="color: #1826ff;">仅为说明本文中所使用的术语：</p>
<ul style="color: #1826ff;" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml">
    <li><em>解析器</em>是指解释 XML 文本文档结构的程序
    </li>
    <li><em>文档表示</em>是指程序用于内存中文档的数据结构
    </li>
    <li><em>文档模型</em>是指支持使用文档表示的库和 API </li>
</ul>
<p style="color: #1826ff;">某些 XML 应用程序根本不需要使用文档模型。如果应用程序可以通过文档的一次遍历搜集它需要的信息，则可能直接使用解析器。该方法可能需要增加一些工作量，但是它的性能总是优于在内存中构建文档表示。</p>
<p style="color: #1826ff;"><a name="N10082">DOM</a><br>DOM（&#8220;文档对象模型&#8221;）是用与平台和语言无关的方式表示 XML 文档的官方 W3C 标准。对于任何 Java 特定的模型，它是很好的对照。为了值得与 DOM 标准分开，Java 特定模型应该提供比 Java DOM 实现更优越的性能和／或易用性的优势。</p>
<p style="color: #1826ff;">DOM 定义充分利用了 XML 文档不同组件的接口和继承性。这为开发者带来了将公共接口用于几个不同类型组件的优势，但是同时增加了 API 的复杂性。因为 DOM 是与语言无关的，所以接口不需要利用公共 Java 组件，例如，Collections 类。</p>
<p style="color: #1826ff;">本文涉及两个 DOM 实现：Crimson 和 Xerces Java。Crimson 是基于 Sun Project X 解析器的
Apache 项目。它合并一个包含 DTD 支持的完整验证解析器。可以通过 SAX2 接口访问该解析器，并且 DOM 实现可以与其它 SAX2
解析器一起工作。Crimson 是在 Apache 许可证下发布的开放源码。用于性能比较的版本是 Crimson 1.1.1（jar
文件大小是 0.2MB），它包含有用于从文本文件的 DOM 构建的 SAX2 解析器。</p>
<p style="color: #1826ff;">另一个测试的 DOM 实现，即 Xerces Java 是另一个 Apache 项目。初始时，Xerces 基于 IBM Java
解析器（通常称为 XML4J）。（当前还处于早期 beta 测试版的重新开发的 Xerces Java 2 将最终继承它。当前版本有时称为
Xerces Java 1。）如同使用 Crimson 一样，可以通过 SAX2 接口和 DOM 来访问 Xerces
解析器。然而，Xerces 不提供将 Xerces DOM 与不同的 SAX2 解析器一起使用的任何方法。Xerces Java 包含对
DTD 和 XML Schema 的验证支持（仅带有对 Schema 支持的最小限制）。</p>
<p style="color: #1826ff;">Xerces Java 还支持 DOM 的延迟节点扩展方式（请参考本文中的 <em xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml">延迟 Xerces</em>或 <em xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml">Xerces def.</em>），
其中文档组件初始时是以压缩格式表示的，仅当使用时才将它扩展成完整的 DOM
表示。这种方式的用意是允许更快的解析并降低内存的使用，尤其对于那些可能仅使用部分输入文档的应用程序。与 Crimson 类似，Xerces
是在 Apache 许可证下发布的开放源码。用于性能比较的版本是 Xerces 1.4.2（jar 文件大小是 1.8MB）。 </p>
<p style="color: #1826ff;"><a name="N1009D">JDOM</a><br>JDOM
的目的是成为 Java 特定文档模型，它简化与 XML 的交互并且比使用 DOM 实现更快。由于是第一个 Java 特定模型，JDOM
一直得到大力推广和促进。正在考虑通过&#8220;Java 规范请求 JSR-102&#8221;将它最终用作&#8220;Java
标准扩展&#8221;。虽然实际将采用的格式仍在开发中，还是对两个 beta 测试版的 JDOM API 做了很大的更改，。从 2000 年初就已经开始了
JDOM 开发。</p>
<p style="color: #1826ff;">JDOM 与 DOM 主要有两方面不同。首先，JDOM 仅使用具体类而不使用接口。这在某些方面简化了 API，但是也限制了灵活性。第二，API 大量使用了 Collections 类，简化了那些已经熟悉这些类的 Java 开发者的使用。</p>
<p style="color: #1826ff;">JDOM 文档声明其目的是&#8220;使用 20%（或更少）的精力解决 80%（或更多）Java/XML 问题&#8221;（根据学习曲线假定为
20%）。JDOM 对于大多数 Java/XML 应用程序来说当然是有用的，并且大多数开发者发现 API 比 DOM 容易理解得多。JDOM
还包括对程序行为的相当广泛检查以防止用户做任何在 XML 中无意义的事。然而，它仍需要您充分理解 XML
以便做一些超出基本的工作（或者甚至理解某些情况下的错误）。这也许是比学习 DOM 或 JDOM 接口都更有意义的工作。</p>
<p style="color: #1826ff;">JDOM 自身不包含解析器。它通常使用 SAX2 解析器来解析和验证输入 XML 文档（尽管它还可以将以前构造的 DOM
表示作为输入）。它包含一些转换器以将 JDOM 表示输出成 SAX2 事件流、DOM 模型或 XML 文本文档。JDOM 是在 Apache
许可证变体下发布的开放源码。用于性能比较的版本是 JDOM Beta 0.7（jar 文件大小是 0.1MB）它带有用于从文本文件构建
JDOM 表示的 Crimson SAX2 解析器。</p>
<p style="color: #1826ff;"><a name="N100AF">dom4j</a><br>虽然 dom4j
代表了完全独立的开发结果，但最初，它是 JDOM 的一种智能分支。它合并了许多超出基本 XML 文档表示的功能，包括集成的 XPath
支持、XML Schema 支持（当前为 alpha 格式）以及用于大文档或流化文档的基于事件的处理。它还提供了构建文档表示的选项，它通过
dom4j API 和标准 DOM 接口具有并行访问功能。从 2000 下半年开始，它就一直处于开发之中，保留了最近发行版之间的现有 API。</p>
<p style="color: #1826ff;">为支持所有这些功能，dom4j 使用接口和抽象基本类方法。dom4j 大量使用了 API 中的 Collections
类，但是在许多情况下，它还提供一些替代方法以允许更好的性能或更直接的编码方法。直接好处是，虽然 dom4j 付出了更复杂的 API
的代价，但是它提供了比 JDOM 大得多的灵活性。</p>
<p style="color: #1826ff;">在添加灵活性、XPath 集成和对大文档处理的目标时，dom4j 的目标与 JDOM 是一样的：针对 Java
开发者的易用性和直观操作。它还致力于成为比 JDOM 更完整的解决方案，实现在本质上处理所有 Java/XML
问题的目标。在完成该目标时，它比 JDOM 更少强调防止不正确的应用程序行为。</p>
<p style="color: #1826ff;">dom4j 使用相同方法作为 JDOM 输出，这依靠 SAX2 解析器输入处理，依靠转换器将输出处理成 SAX2 事件流、DOM 模型或
XML 文本文档。dom4j 是在 BSD 样式许可证下发布的开放源码，该许可证本质上等价于 Apache 样式许可证。用于性能比较的版本是
dom4j 0.9（jar 文件大小是 0.4MB），带有用于从文本文件构建表示的受绑定 AElfred SAX2 解析器（由于 SAX2
选项设置，测试文件之一无法由 dom4j 使用用于 JDOM 测试的同一 Crimson SAX2 解析器来处理）。</p>
<p style="color: #1826ff;"><a name="N100C1">Electric XML</a><br>Electric
XML（EXML）是支持分布式计算的商业项目的附属产物。它与目前为止讨论的其它模型的不同之处在于，它只能适当地支持 XML
文档的子集，它没有为验证提供任何支持并且有更严格的许可证。然而，EXML 的优势是大小很小并提供了对 XPath
子集的直接支持，因为在最近几篇文章中已经将它提升其它模型的替代模型，所以通过该比较使它成为一个引人注意的候选。</p>
<p style="color: #1826ff;">虽然 EXML 通过使用抽象的基本类方法取得了某些相同效果，但它在避免使用接口方面使用与 JDOM
类似的方法（主要区别是接口为扩展实现提供了更大的灵活性）。它与 JDOM 的不同之处还在于避免使用 Collections
类。该组合为其提供了非常简单的 API，在许多方面类似于带有附加 XPath 操作的 DOM API 简化版本。</p>
<p style="color: #1826ff;"><em xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml">仅</em>当
空白与非空白文本内容邻近时，EXML 才在文档中保留空白，这就将 EXML 限制成 XML 文档的一个子集。标准 XML
需要在读取文档时保留该空白，除非对文档 DTD 可以确认有无空白无关紧要。对于事先已经知道空白无关紧要的许多 XML 应用程序来说，EXML
方法工作得很好，但是它防止对于期望保留空白的文档（例如，生成由浏览器显示或查看的文档的应用程序）使用
EXML。（有关作者对于该主题的谦虚建议，请参阅副栏 <a href="http://www-128.ibm.com/developerworks/cn/xml/x-injava/index.html#whitespaceSB" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml">使用空白的目的</a>。） </p>
<p style="color: #1826ff;">这种空白的删除会对性能比较产生误导效果 ― 许多类型的测试范围与文档中的组件个数成比例，并且由 EXML 删除的每个空白序列都是其它模型中的组件。EXML 包含在本文显示的结果中，但是解释性能差异时请记住这种影响。</p>
<p style="color: #1826ff;">EXML 使用集成的解析器依据文本文档构建文档表示。除了通过文本方式外，它不提供从 DOM（或 SAX2）转换或转换成 SAX2（或
DOM）事件流的任何方式。EXML 是由 Mind Electric
在禁止将它嵌入某些类型的应用程序或库的受限许可证下发布的开放源码。用于性能比较的版本是 Electric XML 2.2（jar 文件大小是
0.05MB）。</p>
<p style="color: #1826ff;"><a name="N100DD">XML Pull Parser</a><br>XML
Pull Parser (XPP）是最近开发的，它演示了 XML 解析的不同方法。与 EXML 一样，XPP 只能适当支持 XML
文档的子集并且不提供验证的任何支持。它同样具有尺寸小的优势。这种优势再与拉回解析器方法结合，使它成为该比较中的良好替换项。</p>
<p style="color: #1826ff;">XPP 几乎独占地使用接口，但是它仅使用所有类中的一小部分。和 EXML 一样，XPP 避免使用 API 中的 Collections 类。总的来说，它是本文中最简单的文档模型 API。</p>
<p style="color: #1826ff;">将 XPP 限制成 XML 文档子集的局限性是它不支持文档中的实体、注释或处理指示信息。XPP
创建仅包含元素、属性（包括&#8220;名称空间&#8221;）和内容文本的文档结构。这对于某些类型的应用程序来说是一种非常严格的限制。但是通常它对性能的影响比
EXML 空白处理对性能的影响小。在本文中我仅使用了一个与 XPP 不兼容的测试文件，并且在带有注释的图表中显示了 XPP
结果，该注释不包含该文件。</p>
<p style="color: #1826ff;">XPP 中的拉回解析器支持（本文中称为 <em xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml">XPP 拉回</em>）
通过将解析实际上推迟到访问文档的一个组件时才进行，然后按照构造那个组件的需要对文档进行解析。该技术想实现允许非常快速的文档显示或分类应用，尤其在
需要转发或除去（而不是对文档进行完全解析和处理）文档时。该方法的使用是可选的，如果以非拉回型方式使用
XPP，它对整个文档进行解析并且同时地构建完整的表示。 </p>
<p style="color: #1826ff;">与 EXML 一样，XPP 使用依据文本文档构建文档表示的集成语法解析器，并且除了通过文本方式外，它不提供从 DOM（或
SAX2）转换或转换成 SAX2（或 DOM）事件流的任何方式。XPP 是具有 Apache 样式许可证的开放源代码。用于性能比较的版本是
PullParser 2.0.1 Beta 8（jar 文件大小是 0.04MB）。</p>
<p style="color: #1826ff;">
<table align="right" border="1" cellpadding="5" cellspacing="0" width="30%">
    <tbody>
        <tr>
            <td background="/developerworks/cn/i/bg-gold.gif">
            <p><a name="testSB"><strong>测试详细信息</strong></a><br>所显示的计时结果是来自使用 Sun Microsystems
            Java version 1.3.1、Java HotSpot Client VM 1.3.1-b24 测试，这些软件是运行在带有 256MB
            RAM 的 Athlon 1GHz 系统上的 Redhat Linux 7.1 下。将这些测试的初始 JVM 和最大内存大小都设置成
            128MB，我想将它表示为服务器类型执行环境。</p>
            <p>在使用初始缺省 JVM 内存设置为 2MB 和最大内存为 64MB 运行的测试中，带有较大 jar 文件大小（DOM、JDOM 和
            dom4j）的模型的结果非常差，尤其在运行测试的平均时间中。这可能是由于内存受限执行的 HotSpot JVM 的无效操作引起的。</p>
            <p>文档模型中的两种（XPP 和
            EXML）支持直接将文档输入成&#8220;字符串&#8221;或字符数组。该类型直接输入不能代表实际应用程序，因此我在这些测试中避免使用它。对于输入和输出，我使用
            Java 流封装字节以消除 I/O 对性能的影响，而保留了用于 XML 文档输入和输出的应用程序在典型情况下使用的语言接口。 </p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p style="color: #1826ff;"><a name="N10107">性能比较</a><br>本文中使用的性能比较基于对一组选中的 XML 文档进行的解析和使用，这些文档试图代表较大范围的应用程序：</p>
<ul style="color: #1826ff;" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml">
    <li>much_ado.xml，标记成 XML 的莎士比亚戏剧。没有属性并且是相当简单的结构（202K 字节）。
    </li>
    <li>periodic.xml, XML 中的元素的周期表。一些属性，也是相当简单的（117K 字节）。
    </li>
    <li>soap1.xml，取自规范的样本 SOAP 文档。大量名称空间和属性（0.4K 字节，每次测试需要重复 49 次）。
    </li>
    <li>soap2.xml，SOAP 文档格式中的值列表。大量名称空间和属性（134K 字节）。
    </li>
    <li>nt.xml，标记为 XML 的&#8220;新约&#8221;。没有属性并且非常简单的结构，大量文本内容（1047K 字节）。
    </li>
    <li>xml.xml，XML 规范，不带 DTD 引用，在内部定义所有实体。带有大量混合内容的文本样式标记，一些属性（160K 字节）。 </li>
</ul>
<p style="color: #1826ff;">关于测试平台的更多信息，请参阅副栏 <a href="http://www-128.ibm.com/developerworks/cn/xml/x-injava/index.html#testSB" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml">测试详细信息</a>并查看 <a href="http://www-128.ibm.com/developerworks/cn/xml/x-injava/index.html#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml">参考资料</a>以获取用于测试的源代码的链接。 </p>
<p style="color: #1826ff;">除了非常小的 soap1.xml 文档之外，所有评测时间都是指文档的每次特定测试所经历的时间。在 soap1.xml 的情况下，评测的时间是 49 个连续的文档测试（总数为 20K 字节文本的足够副本数）。</p>
<p style="color: #1826ff;">测试框架在一个文档上运行一个特定的测试多次（这里显示运行了 10
次），依此跟踪该测试的最短时间和平均时间，然后继续同一文档上的下一个测试。完成对一个文档的全部测试序列后，它对下一个文档重复该过程。为防止文档模
型之间的交互，在执行每个测试框架时仅测试一个模型。</p>
<p style="color: #1826ff;">HotSpot 以及类似于动态优化 JVM
的计时基准程序是出了名的棘手的；测试序列中的小变化经常导致计时结果发生很大变化。我已经发现对于执行特定代码段的平均时间时，确实如此；最短时间比较
一致，正是我在这些结果中列出的值。可以参阅第一次测试（文档构建时间）的 <a href="http://www-128.ibm.com/developerworks/cn/xml/x-injava/sideavgcomp.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml">平均和最短时间</a>的比较。 </p>
<p style="color: #1826ff;"><a name="N1013D">文档构建时间</a><br>文档构建时间测试检
查解析文本文档和构造文档表示所需的时间。出于比较目的，已经在图表中包含了使用 Crimson 和 Xerces SAX2 解析的 SAX2
解析时间，因为大多数文档模型（除了 EXML 和 XPP 外的所有文档）使用 SAX2 解析事件流作为文档构建过程的输入。图 1
描述了测试结果。</p>
<p style="color: #1826ff;"><a name="figure1"><strong>图 1. 文档构建时间</strong></a><br><img alt="文档构建时间图" src="http://www.360doc.com/DownloadImg/73/16542_1.gif" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" height="608" width="600"> </p>
<p style="color: #1826ff;">对于大多数测试文档来说，XPP 拉回的构建时间太短以至于难以计算（因为在这种情况下，实际上没有对该文档进行解析），只显示为非常短的
soap1.xml。对于该文件，拉回解析器内存大小和相关的创建开销使 XPP
显得相对比较缓慢。这是因为测试程序为正在进行解析的文档的每个副本创建一个新的拉回解析器副本。在 soap1.xml 情况下，每次评测时间使用
49 个副本。分配与初始化这些解析器实例的开销大于重复解析文本并构建文档表示的大多数其它方法所需的时间。</p>
<p style="color: #1826ff;">XPP 的作者在一个电子邮件的讨论中指出，在实际应用程序中，可以合用拉回解析器实例以重新使用。如果这样做的话，soap1.xml 文件的开销将明显降到忽略不计程度。对于更大的文件，甚至不需要合用，拉回解析器创建开销也可以变得忽略不计。</p>
<p style="color: #1826ff;">在本测试中，XPP（带有完整解析），带有延迟节点创建的 Xerces 和 dom4j 都显示整体上的同等性能。延迟的 Xerces
对于较大的文档尤其出色，但是对于较小的文档的开销较高 ― 甚至比常规 Xerces DOM
高很多。在第一次使用文档的一部分时，延迟节点创建方法的开销也较高，这会降低快速解析的优势。</p>
<p style="color: #1826ff;">对于较小的 soap1.xml 文件，所有格式（SAX2 解析、常规 DOM 和延迟 DOM）的 Xerces
的显得开销较高。对于该文件 XPP（完全解析）尤其出色，对于 soap1.xml，EXML 甚至超过基于 SAX2 的模型。虽然 EXML
具有废弃单独的空白内容的优势，但是总体上，它是本测试中最差的。</p>
<p style="color: #1826ff;"><a name="N10161">文档遍历时间</a><br>文档遍历时间测试检
查遍历构造的文档表示所需的时间，按文档顺序遍历每个元素、属性和文本内容段。它试图表示文档模型接口的性能，这对于从进行过解析的文档中重复访问信息的
应用程序来说可能很重要。总体上，遍历时间比解析时间快得多。对于只对解析过的文档单次遍历的应用程序，解析时间将比遍历时间更重要。图 2
显示了结果。</p>
<p style="color: #1826ff;"><a name="figure2"><strong>图 2. 文档遍历时间</strong></a><br><img alt="文档遍历时间表" src="http://www.360doc.com/DownloadImg/73/16542_2.gif" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" height="495" width="600"> </p>
<p style="color: #1826ff;">在本测试中，XPP 的性能大大超过了其余的测试对象。Xerces DOM 所花费的时间大约是 XPP 的两倍。虽然 EXML 具有废弃文档中单独的空白内容的优势，但是，EXML 花费的时间几乎是 XPP 的三倍。dom4j 在这张图中处于中间位置。</p>
<p style="color: #1826ff;">使用 XPP
拉回时，直到访问文档表示时才真正发生对文档文本的解析。这导致第一次遍历文档表示时的开销非常大（表中未显示）。如果以后访问整个文档表示，则当使用拉
回解析方法时，XPP 显示性能的净损失。对于拉回解析器来说，前两个测试所需的总时间比使用 XPP 的常规解析长（长 20% 到
100%，这取决于文档）。但是，当还未完全访问正在进行解析的文档时，拉回解析器方法仍然具有可观的性能优势。</p>
<p style="color: #1826ff;">带有延迟节点创建的 Xerces 显示了相似的行为，第一次访问文档表示时导致性能下降（图中未显示）。但是，在 Xerces
情况下，节点创建开销大约与解析期间常规 DOM 创建的性能差值相同。对于较大的文档来说，用 Xerces
延迟的前两次测试所需的总时间大致与使用带常规 DOM 构建的 Xerces 所用的时间相同。如果在非常大的文档（可能 10KB 或更大）上使用
Xerces，则延迟的节点创建似乎是一个好选择。</p>
<p style="color: #1826ff;"><a name="N10182">文档修改时间</a><br>这个测试检查系统地修改构造文档表示所需的时间，其结果在 <a href="http://www-128.ibm.com/developerworks/cn/xml/x-injava/index.html#figure3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml">图 3</a>中
显示。它遍历表示，删除所有单独的空白内容并且用新添加的元素封装每个非空白内容字符串。它还向包含非空白内容的原始文档的每个元素中添加一个属性。该测
试试图表示经过一定范围文档修改后文档模型的性能。如遍历时间一样，修改时间比解析时间短很多。因此，对于仅单次遍历每个解析过的文档的应用程序来说，解
析时间将更重要。 </p>
<p style="color: #1826ff;"><a name="figure3"><strong>图 3. 文档修改时间</strong></a><br><img alt="文档修改时间表" src="http://www.360doc.com/DownloadImg/73/16542_3.gif" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" height="507" width="600"> </p>
<p style="color: #1826ff;">这次测试中 EXML 处于领先地位，但是由于在解析期间它总是废弃单独的空白内容，它才比其它模型具有性能上的优势。这意味着在测试期间没有要从 EXML 表示中进行删除的内容。</p>
<p style="color: #1826ff;">在修改性能方面，XPP 仅次于 EXML，并且与 EXML 不同，XPP 测试包含删除。Xerces DOM 和 dom4j 接近地处于中间位置，JDOM 和 Crimson DOM 模型的性能仍是最差。</p>
<p style="color: #1826ff;"><a name="N101A4">文档生成时间</a><br>这个测试检查将文档表示输出成文本 XML 文档所需的时间；结果显示在 <a href="http://www-128.ibm.com/developerworks/cn/xml/x-injava/index.html#figure4" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml">图 4</a>中。对于不专门使用 XML 文档的任何应用程序，该步骤似乎是整体性能的一个重要部分，特别是因为将文档输出为文本所需的时间总体接近于对文档输入进行解析所需的时间。为使这些时间具有直接的可比性，该测试使用原始文档，而没有使用由前面的测试所生成的已修改文档。 </p>
<p style="color: #1826ff;"><a name="figure4"><strong>图 4. 文本生成时间</strong></a><br><img alt="文本生成时间表" src="http://www.360doc.com/DownloadImg/73/16542_4.gif" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" height="507" width="600"> </p>
<p style="color: #1826ff;">文本生成时间测试表明各模型之间的差别小于前面测试中各项的差别，Xerces DOM 性能最好，但领先不多，JDOM 性能最差。EXML 的性能优于 JDOM，但是这同样是由于 EXML 废弃空白内容。</p>
<p style="color: #1826ff;">许多模型提供了控制文本输出格式的选项，并且一些选项似乎影响文本生成时间。这个测试只使用每个模型的最基本的输出格式，因此结果只显示缺省性能而不显示最好的可能性能。</p>
<p style="color: #1826ff;"><a name="N101C6">文档内存大小</a><br>这个测试检查用于文档表示的内存空间。这对于使用大文档或同时使用多个较小文档的开发者来说，意义尤为重要。图 5 显示这个测试的结果。</p>
<p style="color: #1826ff;"><a name="figure5"><strong>图 5. 文档内存大小</strong></a><br><img alt="文档内存大小表" src="http://www.360doc.com/DownloadImg/73/16542_5.gif" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" height="535" width="600"> </p>
<p style="color: #1826ff;">内存大小结果与计时测试不同，因为小的 soap1.xml 文件显示的值表示文件的单个副本而不表示在计时评测中使用的 49 个副本。在大多数模型中，用于简要文档的内存太小以至于无法在图的刻度上显示。</p>
<p style="color: #1826ff;">除了 XPP 拉回（直到访问它时才真正构建文档表示）之外，与一些计时测试中显示的差别相比，内存大小测试中模型之间的差别相对较小。延迟的
Xerces 具有最紧凑的表示（当第一次访问表示时将它扩展成基本 Xerces 大小），紧接着是 dom4j。虽然 EXML
废弃包含在其它模型中的空白内容，但是它仍具有最不紧凑的表示。</p>
<p style="color: #1826ff;">因为即使最紧凑的模型也要占用大约原始文档文本大小（以字节计）四倍的空间，所以对于大文档来说，所有模型似乎都需要太多的内存。通过提供使用部分
文档表示的方法，XPP 拉回和 dom4j 为非常大的文档提供了最好的支持。XPP 拉回通过仅构建实际被访问的表示部分完成该任务，而
dom4j 包含对基于事件处理的支持，使得一次只构建或处理文档的一部分。</p>
<p style="color: #1826ff;"><a name="N101E7">Java 序列化</a><br>这些测试评测文
档表示的 Java 序列化的时间和输出大小。这主要涉及那些使用 Java RMI（&#8220;远程方法调用&#8221;）在 Java
程序之间传送表示的应用程序（包括 EJB (Enterprise JavaBean) 应用程序）起作用。在这些测试中，仅包含了那些支持
Java 序列化的模型。下列三张图显示了该测试的结果。</p>
<p style="color: #1826ff;"><a name="figure6"><strong>图 6. 序列化输出时间</strong></a><br><img alt="序列化输出时间表" src="http://www.360doc.com/DownloadImg/73/16542_6.gif" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" height="422" width="600"> </p>
<p style="color: #1826ff;"><a name="figure7"><strong>图 7. 序列化输入时间</strong></a><br><img alt="序列化输入时间表" src="http://www.360doc.com/DownloadImg/73/16542_7.gif" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" height="422" width="600"> </p>
<p style="color: #1826ff;"><a name="figure8"><strong>图 8. 序列化文档大小</strong></a><br><img alt="序列化文档大小表" src="http://www.360doc.com/DownloadImg/73/16542_8.gif" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" height="442" width="600"> </p>
<p style="color: #1826ff;">dom4j 显示了输出（生成序列化的格式）和输入（从序列化的格式重新构建文档）的最好的序列化性能，而 Xerces DOM
显示了最差的性能。EXML 所花费的时间接近 dom4j，但是 EXML 还是具有在表示中使用较少数量对象的优势，因为它废弃空白内容。</p>
<p style="color: #1826ff;">如果将文档输出成文本然后进行解析以重新构建文档，而不是使用 Java 序列化，则所有性能 ― 时间和大小 ―
都会好得多。这里的问题是作为大量唯一的小对象的 XML 文档表示的结构。Java
序列化无法有效处理这种类型的结构，这导致时间和输出大小的开销都很高。</p>
<p style="color: #1826ff;"><em xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml">可以</em>设计比文本表示小且比文本输入和输出快的文档序列化格式，但是只能通过绕过 Java 序列化来完成。（我有一个项目实现 XML 文档的这种定制的序列化，在我公司的 Web 站点上可以找到其开放源码，请参阅 <a href="http://www-128.ibm.com/developerworks/cn/xml/x-injava/index.html#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml">参考资料</a>。） </p>
<p style="color: #1826ff;"><a name="N1022D">结束语</a><br>不同的 Java XML 文档模型各有所长，但是从性能观点来看，有些模型具有明显的优势。</p>
<p style="color: #1826ff;">在大多数方面，XPP 性能处于领先地位。尽管 XPP 是一种新模型，但是对于不需要验证、实体、处理指示信息或注释的中间件类型应用程序来说，它是非常好的选择。它尤其适用于作为浏览器小应用程序或在内存受限的环境下运行的应用程序。</p>
<p style="color: #1826ff;">虽然 dom4j 没有与 XPP 同等的速度，但是，它确实提供了具备更标准化的优越性能和功能更全的实现，包括对 SAX2、DOM 甚至
XPath 的内置支持。虽然 Xerces DOM（带有延迟的节点创建）对于小文件和 Java
序列化性能不佳，但是在大多数评测时仍然出色。对于常规 XML 处理，dom4j 和 Xerces DOM
都是很好的选择，对它们的选择取决于您认为是特定于 Java 的特性更重要还是跨语言的兼容性更重要。</p>
<p style="color: #1826ff;">JDOM 和 Crimson DOM 在性能测试时一直表现不佳。在小文档情况下还值得考虑使用 Crimson DOM，而 Xerces
表现很差。虽然 JDOM 的开发者已经说明他们期望在正式发行版前专注性能问题，但是从性能观点来看，它确实没有值得推荐之处。然而，如果不进行
API 的重新构建，JDOM 可能难以达到与其它模型匹配的性能。</p>
<p style="color: #1826ff;">
<table align="right" border="1" cellpadding="5" cellspacing="0" width="30%">
    <tbody>
        <tr>
            <td background="/developerworks/cn/i/bg-gold.gif">
            <p><a name="whitespaceSB"><strong>使用空白的目的</strong></a><br>XML 规范通常需要保留空白，但是许多 XML 应用程序使用仅为可读性而保留空白的格式。对于这些应用程序，EXML 废弃隔离空白的方法起了作用。</p>
            <p>这些性能中使用的大多数文档属于&#8220;为可读性而保留的空白&#8221;类别。这些文档被格式化成便于人们查看的形式，一行最多一个元素。结果，无关的空白内容字符串数实际上超过了文档中的元素数量。这大大增加了每一步处理的不必要开销。</p>
            <p>支持修剪输入上这种类型空白的选项将有助于提高带有可忽略的空白的应用程序的所有文档模型的性能（除了 EXML
            之外）。只要修剪是一个选项，它就不会影响需要完全保留空白的应用程序。解析器级别的支持将更好，因为解析器必须逐一处理输入字符。总之，这种类型的选项
            将非常有助于许多 XML 应用程序。</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p style="color: #1826ff;">EXML 非常小（以 jar 文件大小为单位）并且在一些性能测试中表现良好。虽然 EXML 具有删除单独空白内容的优势，但是在性能方面不如 XPP。除非您需要 EXML 支持而 XPP 缺少的一种特性，否则在内存受限的环境下，XPP 可能是更好的选择。</p>
<p style="color: #1826ff;">虽然 dom4j 性能最好，但是，当前没有一种模型能为 Java 序列化提供良好性能。如果需要在程序之间传递文档，通常的最佳选择是将文档写成文本然后进行解析以重新构建表示。将来，定制序列化格式可能提供一个更好的选择。</p>
<p style="color: #1826ff;"><a name="N10257">后续内容...</a><br>我已经涵盖了一些文档模型的基本特性，并且显示了几种类型文档操作的性能评测。请记住，虽然，性能只是选择文档模型的一个因素。对于大多数开发者，可用性至少与性能一样重要，并且这些模型使用不同的 API 都可能有喜欢这个而不喜欢那个的理由。</p>
<p style="color: #1826ff;">在后续文章中将集中研究可用性，其中我将比较用于完成这些不同模型中相同操作的样本代码。请检查本比较的第二部分。当您等待时，可以通过下面的论坛中的链接提出您对本文的评论和问题与大家共享。</p>
<p style="color: #1826ff;"><a name="resources">参考资料 </a>
</p>
<ul style="color: #1826ff;">
    <li>您可以参阅本文在 developerWorks 全球站点上的 <a href="http://www.ibm.com/developerworks/library/x-dynweb/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml">英文原文</a>. <br><br>
    </li>
    <li>参加关于本文的 <a href="javascript:void forumwindow()" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml">论坛</a>。 <br><br>
    </li>
    <li>如果您需要背景知识，请尝试 developerWorks的教程 <a href="http://www-900.ibm.com/developerworks/cn/cnedu.nsf/xml-onlinecourse-bytitle/E5B3571D78D0821748256A77001189ED?OpenDocument" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml">XML Java 编程</a>、 <a href="http://www-900.ibm.com/developerworks/cn/cnedu.nsf/xml-onlinecourse-bytitle/CA45E09F1E2EF41E48256B1B000C6C7B?OpenDocument" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml">理解 SAX</a>和 <a href="http://www-900.ibm.com/developerworks/cn/cnedu.nsf/xml-onlinecourse-bytitle/386674F65A47844C48256BD10023D453?OpenDocument" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml">理解 DOM</a>。 <br><br>
    </li>
    <li>从 <a href="http://www-128.ibm.com/developerworks/cn/xml/x-injava/sidedownload.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml">下载页面</a>下载用于本文的测试程序和文档模型库。 <br><br>
    </li>
    <li>在测试程序的 <a href="http://www.sosnoski.com/opensrc/xmlbench/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml">主页</a>上查看更新的测试和测试结果。 <br><br>
    </li>
    <li>获取作者关于 <a href="http://www.sosnoski.com/opensrc/xmls/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml">XML Serial (XMLS) encoding</a>工作的详细信息作为 Java 序列化的替代项。 <br><br>
    </li>
    <li>研究或下载本文中讨论的 Java XML 文档模型：
    <ul xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml">
        <li><a href="http://xml.apache.org/xerces-j/index.html">Xerces Java</a>
        </li>
        <li><a href="http://xml.apache.org/crimson/index.html">Crimson</a>
        </li>
        <li><a href="http://jdom.org/index.html">JDOM</a>
        </li>
        <li><a href="http://dom4j.org/index.html">dom4j</a>
        </li>
        <li><a href="http://www.themindelectric.com/products/xml/xml.html">Electric XML（EXML）</a>
        </li>
        <li><a href="http://www.extreme.indiana.edu/soap/xpp/">XML Pull Parser（XPP）</a> </li>
    </ul>
    <br>
    </li>
    <li>IBM WebSphere Application Server 包含基于 Xerces Java 的 XML4J 解析器。可在 <a href="http://www.ibm.com/developerworkshttp://www-4.ibm.com/software/webservers/appserv/doc/v30/ae/web/doc/begin_here/index.html&amp;origin=x" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml">WAS Advanced edition 3.0 online documentation</a>中找到关于产品 XML 支持的 how-to 信息。 <br></li>
</ul>
<a name="author1"></a>的
创建者和首席顾问。他具有 30 多年的专业软件开发经验，最近几年，他集中研究服务器方 Java 技术，包括
servlet、Enterprise JavaBeans 和 XML。他已经多次演示了 Java 性能问题和常规服务器端的 Java
技术，他还是 </span><a style="color: #1826ff;" href="http://www.sosnoski.com/jxml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml">Seattle Java-XML SIG</a><span style="color: #1826ff;">的主席。
</span><br><img src ="http://www.blogjava.net/sutao/aggbug/141000.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sutao/" target="_blank">苏醄</a> 2007-08-29 17:17 <a href="http://www.blogjava.net/sutao/articles/141000.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA的IO系统</title><link>http://www.blogjava.net/sutao/articles/140910.html</link><dc:creator>苏醄</dc:creator><author>苏醄</author><pubDate>Wed, 29 Aug 2007 06:41:00 GMT</pubDate><guid>http://www.blogjava.net/sutao/articles/140910.html</guid><wfw:comment>http://www.blogjava.net/sutao/comments/140910.html</wfw:comment><comments>http://www.blogjava.net/sutao/articles/140910.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sutao/comments/commentRss/140910.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sutao/services/trackbacks/140910.html</trackback:ping><description><![CDATA[<a style="color: #2e10ff;" href="http://blog.csdn.net/meconsea/archive/2005/04/11/343468.aspx">迷你javaio系统</a>
<div style="color: #2e10ff;" class="postText">
<p>java中的io系统<br>io中的（input/output）stream无非就是包括基于字符的stream、基于字节的stream和把字节导向的stream转换<br>字符为导向的stream的stream。（很难理解么？）<br>以字节为导向的stream------InputStream/OutputStream<br>InputStream 和 OutputStream是两个abstact类，对于字节为导向的stream都扩展这两个鸡肋（基类^_^）;<br>--InputStream<br>&nbsp; ByteArrayInputStream -- 把内存中的一个缓冲区作为InputStream使用.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; construct---ByteArrayInputStream(byte[])创建一个新字节数组输入流，它从指定字节数组中读取数据。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ---ByteArrayInputStream(byte[], int, int) 创建一个新字节数组输入流，它从指定字节数组中读取数据。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ---mark::该字节数组未被复制。<br>&nbsp; <br>&nbsp; StringBufferInputStream -- 把一个String对象作为InputStream .<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注释：不推荐使用 StringBufferInputStream 方法。 此类不能将字符正确的转换为字节。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 同 JDK 1.1 版中的类似，从一个串创建一个流的最佳方法是采用 StringReader 类。 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; construct---StringBufferInputStream(String) 据指定串创建一个读取数据的输入流串。<br>&nbsp; <br>&nbsp; FileInputStream -- 把一个文件作为InputStream，实现对文件的读取操作 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; construct---FileInputStream(File) 创建一个输入文件流，从指定的 File 对象读取数据。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ---FileInputStream(FileDescriptor) 创建一个输入文件流，从指定的文件描述器读取数据。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ---FileInputStream(String) 创建一个输入文件流，从指定名称的文件读取数据。<br>&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; method ---- read() 从当前输入流中读取一字节数据。 <br>&nbsp;&nbsp;&nbsp; read(byte[]) 将当前输入流中 b.length 个字节数据读到一个字节数组中。 <br>&nbsp;&nbsp;&nbsp; read(byte[], int, int) 将输入流中 len 个字节数据读入一个字节数组中。<br>&nbsp;&nbsp;&nbsp; <br>&nbsp; PipedInputStream：实现了pipe的概念，主要在线程中使用. 管道输入流是指一个通讯管道的接收端。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一个线程通过管道输出流发送数据，而另一个线程通过管道输入流读取数据，<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这样可实现两个线程间的通讯。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PipedInputStream() 创建一个管道输入流，它还未与一个管道输出流连接。 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PipedInputStream(PipedOutputStream) 创建一个管道输入流, 它已连接到一个管道输出流。 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp; SequenceInputStream：把多个InputStream合并为一个InputStream .&#8220;序列输入流&#8221;类允许应用程序把几个输入流连续地合并起来，<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 并且使它们像单个输入流一样出现。每个输入流依次被读取，直到到达该流的末尾。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 然后&#8220;序列输入流&#8221;类关闭这个流并自动地切换到下一个输入流。 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SequenceInputStream(Enumeration) 创建一个新的序列输入流，并用指定的输入流的枚举值初始化它。 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SequenceInputStream(InputStream, InputStream) 创建一个新的序列输入流，初始化为首先 读输入流 s1, 然后读输入流 s2。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>--OutputSteam<br>&nbsp; <br>&nbsp; ByteArrayOutputStream：把信息存入内存中的一个缓冲区中.该类实现一个以字节数组形式写入数据的输出流。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当数据写入缓冲区时，它自动扩大。用 toByteArray() 和 toString() 能检索数据。 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; construct --- ByteArrayOutputStream() 创建一个新的字节数组输出流。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --- ByteArrayOutputStream() 创建一个新的字节数组输出流。 <br>&nbsp;&nbsp;--- ByteArrayOutputStream(int) 创建一个新的字节数组输出流，并带有指定大小字节的缓冲区容量。 <br>&nbsp;&nbsp;toString(String) 根据指定字符编码将缓冲区内容转换为字符串，并将字节转换为字符。 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; write(byte[], int, int) 将指定字节数组中从偏移量 off 开始的 len 个字节写入该字节数组输出流。 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; write(int) 将指定字节写入该字节数组输出流。 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writeTo(OutputStream) 用 out.write(buf, 0, count) 调用输出流的写方法将该字节数组输出流的全部内容写入指定的输出流参数。 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp; FileOutputStream:文件输出流是向 File 或 FileDescriptor 输出数据的一个输出流。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FileOutputStream(File) 创建一个文件输出流，向指定的 File 对象输出数据。 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FileOutputStream(FileDescriptor) 创建一个文件输出流，向指定的文件描述器输出数据。 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FileOutputStream(String) 创建一个文件输出流，向指定名称的文件输出数据。 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FileOutputStream(String, boolean) 用指定系统的文件名，创建一个输出文件。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp; PipedOutputStream:管道输出流是指一个通讯管道的发送端。 一个线程通过管道输出流发送数据，<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 而另一个线程通过管道输入流读取数据，这样可实现两个线程间的通讯。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PipedOutputStream() 创建一个管道输出流，它还未与一个管道输入流连接。 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PipedOutputStream(PipedInputStream) 创建一个管道输出流，它已连接到一个管道输入流。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>以字符为导向的stream Reader/Writer</p>
<p>以Unicode字符为导向的stream，表示以Unicode字符为单位从stream中读取或往stream 中写入信息。<br>Reader/Writer 为abstact类<br>以Unicode字符为导向的stream包括下面几种类型： </p>
<p>－－ Reader</p>
<p>1) CharArrayReader：与ByteArrayInputStream对应 <br>&nbsp;&nbsp; CharArrayReader(char[]) 用指定字符数组创建一个 CharArrayReader。<br>&nbsp;&nbsp; CharArrayReader(char[], int, int) 用指定字符数组创建一个 CharArrayReader。<br>&nbsp;&nbsp; <br>2) StringReader：与StringBufferInputStream对应 <br>&nbsp;&nbsp; StringReader(String) 创建一新的串读取者。<br>3) FileReader：与FileInputStream对应 </p>
<p>4) PipedReader：与PipedInputStream对应 </p>
<p>－－ Writer</p>
<p>1) CharArrayWrite：与ByteArrayOutputStream对应 <br>2) StringWrite：无与之对应的以字节为导向的stream <br>3) FileWrite：与FileOutputStream对应 <br>4) PipedWrite：与PipedOutputStream对应 </p>
<p>两种不现导向的stream之间的转换<br>&nbsp;&nbsp; InputStreamReader和OutputStreamReader：把一个以字节为导向的stream转换成一个以字符为导向的stream。<br>&nbsp;&nbsp; 一个 InputStreamReader 类是从字节流到字符流的桥梁：它读入字节，并根据指定的编码方式，将之转换为字符流。<br>&nbsp;&nbsp; 使用的编码方式可能由名称指定，或平台可接受的缺省编码方式。<br>&nbsp;&nbsp; <br>&nbsp;&nbsp; InputStreamReader 的 read() 方法之一的每次调用，可能促使从基本字节输入流中读取一个或多个字节。<br>&nbsp;&nbsp; 为了达到更高效率，考虑用 BufferedReader 封装 InputStreamReader，<br>&nbsp;&nbsp; BufferedReader in = new BufferedReader(new InputStreamReader(System.in));<br>&nbsp;&nbsp; <br>&nbsp;&nbsp; InputStreamReader(InputStream) 用缺省的字符编码方式，创建一个 InputStreamReader。 <br>&nbsp;&nbsp; InputStreamReader(InputStream, String) 用已命名的字符编码方式，创建一个 InputStreamReader。<br>&nbsp;&nbsp; <br>&nbsp;&nbsp; OutputStreamWriter 将多个字符写入到一个输出流，根据指定的字符编码将多个字符转换为字节。 <br>&nbsp;&nbsp; 每个 OutputStreamWriter 合并它自己的 CharToByteConverter, 因而是从字符流到字节流的桥梁。<br>&nbsp;&nbsp; <br>FilterInputStream、RandomAccessFile 见例子。<br>ObjectInputStream 、 ObjectOutputStream见另外blog。</p>
<p><br>Java IO的一般使用原则：</p>
<p>一、按数据来源（去向）分类：<br>1、是文件： FileInputStream, FileOutputStream, FileReader, FileWriter<br>2、是byte[]：ByteArrayInputStream, ByteArrayOutputStream<br>3、是Char[]: CharArrayReader, CharArrayWriter<br>4、是String: StringBufferInputStream, StringReader, StringWriter<br>5、网络数据流：InputStream, OutputStream, Reader, Writer</p>
<p>二、按是否格式化输出分：<br>1、要格式化输出：PrintStream, PrintWriter</p>
<p>三、按是否要缓冲分：<br>1、要缓冲：BufferedInputStream, BufferedOutputStream, BufferedReader, BufferedWriter</p>
<p>四、按数据格式分：<br>1、二进制格式（只要不能确定是纯文本的）: InputStream, OutputStream及其所有带Stream结束的子类<br>2、纯文本格式（含纯英文与汉字或其他编码方式）；Reader, Writer及其所有带Reader, Writer的子类</p>
<p>五、按输入输出分：<br>1、输入：Reader, InputStream类型的子类<br>2、输出：Writer, OutputStream类型的子类</p>
<p>六、特殊需要：<br>1、从Stream到Reader,Writer的转换类：InputStreamReader, OutputStreamWriter<br>2、对象输入输出：ObjectInputStream, ObjectOutputStream<br>3、进程间通信：PipeInputStream, PipeOutputStream, PipeReader, PipeWriter<br>4、合并输入：SequenceInputStream<br>5、更特殊的需要：PushbackInputStream, PushbackReader, LineNumberInputStream, LineNumberReader</p>
<p>决定使用哪个类以及它的构造进程的一般准则如下（不考虑特殊需要）：<br>首先，考虑最原始的数据格式是什么： 原则四<br>第二，是输入还是输出：原则五<br>第三，是否需要转换流：原则六第1点<br>第四，数据来源（去向）是什么：原则一<br>第五，是否要缓冲：原则三 （特别注明：一定要注意的是readLine()是否有定义，有什么比read, write更特殊的输入或输出方法）<br>第六，是否要格式化输出：原则二</p>
</div>
<br><img src ="http://www.blogjava.net/sutao/aggbug/140910.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sutao/" target="_blank">苏醄</a> 2007-08-29 14:41 <a href="http://www.blogjava.net/sutao/articles/140910.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java 理论与实践: 用弱引用堵住内存泄漏</title><link>http://www.blogjava.net/sutao/articles/138865.html</link><dc:creator>苏醄</dc:creator><author>苏醄</author><pubDate>Thu, 23 Aug 2007 07:56:00 GMT</pubDate><guid>http://www.blogjava.net/sutao/articles/138865.html</guid><wfw:comment>http://www.blogjava.net/sutao/comments/138865.html</wfw:comment><comments>http://www.blogjava.net/sutao/articles/138865.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sutao/comments/commentRss/138865.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sutao/services/trackbacks/138865.html</trackback:ping><description><![CDATA[<table style="color: #4328ff;" border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr valign="top">
            <td width="100%">
            <h1>Java 理论与实践: 用弱引用堵住内存泄漏</h1>
            <p id="subtitle"><em> 弱引用使得表达对象生命周期关系变得容易了</em> </p>
            <img alt="" src="http://www.ibm.com/i/c.gif" class="display-img" height="6" width="1"></td>
            <td class="no-print" width="192"><img alt="developerWorks" src="http://www.ibm.com/developerworks/i/dw.gif" height="18" width="192"></td>
        </tr>
    </tbody>
</table>
<table style="color: #4328ff;" border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr valign="top">
            <td width="10"><img alt="" src="http://www.ibm.com/i/c.gif" height="1" width="10"></td>
            <td width="100%">
            <table class="no-print" align="right" border="0" cellpadding="0" cellspacing="0" width="160">
                <tbody>
                    <tr>
                        <td width="10"><img alt="" src="http://www.ibm.com/i/c.gif" height="1" width="10"></td>
                        <td>
                        <table border="0" cellpadding="0" cellspacing="0" width="150">
                            <tbody>
                                <tr>
                                    <td class="v14-header-1-small">文档选项</td>
                                </tr>
                            </tbody>
                        </table>
                        <table class="v14-gray-table-border" border="0" cellpadding="0" cellspacing="0">
                            <tbody>
                                <tr>
                                    <td class="no-padding" width="150"><img alt="" src="http://www.ibm.com/i/c.gif" height="1" width="8"><input value="虽然用 java 语言编写的程序在理论上是不会出现&#8220;内存泄漏&#8221;的，但是有时对象在不再作为程序的逻辑状态的一部分之后仍然不被垃圾收集。本月，清洁大师 brian goetz 探讨了无意识的对象保留的常见原因，并展示了如何用弱引用堵住泄漏。" name="body" type="hidden"><input name="subject" value="Java 理论与实践: 用弱引用堵住内存泄漏" type="hidden"><input name="lang" value="cn" type="hidden"><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>未显示需要 JavaScript
                                    的文档选项</p>
                                    </td>
                                </tr>
                                </noscript>
                                <table border="0" cellpadding="0" cellspacing="0" width="143">
                                    <form action="https://www.ibm.com/developerworks/secure/email-it.jsp" name="email">
                                    </form>
                                    <script language="JavaScript" type="text/javascript">
<!--
document.write('
<tr valign="top">
<td width="8"><img src="//www.ibm.com/i/c.gif" src_cetemp="//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" src_cetemp="//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();" href_cetemp="javascript:document.email.submit();"><strong>将此页作为电子邮件发送</strong></a></p></td>
</tr>');
//-->
</script>
                                    <tbody>
                                        <tr valign="top">
                                            <td width="8"><img src="http://www.ibm.com/i/c.gif" alt="" height="1" width="8"></td>
                                            <td width="16"><img src="http://www.ibm.com/i/v14/icons/em.gif" alt="将此页作为电子邮件发送" height="16" vspace="3" width="16"></td>
                                            <td width="122">
                                            <p><a class="smallplainlink" href="javascript:document.email.submit();"><strong>将此页作为电子邮件发送</strong></a></p>
                                            </td>
                                        </tr>
                                        <tr valign="top">
                                            <td width="8"><img alt="" src="http://www.ibm.com/i/c.gif" height="1" width="8"></td>
                                            <td width="16"><img alt="" src="http://www.ibm.com/i/v14/icons/fw_bold.gif" border="0" height="16" vspace="3" width="16"></td>
                                            <td width="122">
                                            <p><a class="smallplainlink" href="http://www.ibm.com/developerworks/community/"><strong>讨论</strong></a></p>
                                            </td>
                                        </tr>
                                    </tbody>
                                </table>
                                </td>
                            </tr>
                        </tbody>
                    </table>
                    <!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- this content will be automatically generated across all content areas -->
                    <br><!--end RESERVED FOR FUTURE USE INCLUDE FILES--><br></td>
                </tr>
            </tbody>
        </table>
        <p>级别： 中级</p>
        <p><a href="http://www.ibm.com/developerworks/cn/java/j-jtp11225/#author">Brian Goetz</a> (<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#98;&#114;&#105;&#97;&#110;&#64;&#113;&#117;&#105;&#111;&#116;&#105;&#120;&#46;&#99;&#111;&#109;&#63;&#115;&#117;&#98;&#106;&#101;&#99;&#116;&#61;&#37;&#69;&#55;&#37;&#57;&#52;&#37;&#65;&#56;&#37;&#69;&#53;&#37;&#66;&#67;&#37;&#66;&#49;&#37;&#69;&#53;&#37;&#66;&#67;&#37;&#57;&#53;&#37;&#69;&#55;&#37;&#57;&#52;&#37;&#65;&#56;&#37;&#69;&#53;&#37;&#65;&#48;&#37;&#66;&#53;&#37;&#69;&#52;&#37;&#66;&#68;&#37;&#56;&#70;&#37;&#69;&#53;&#37;&#56;&#54;&#37;&#56;&#53;&#37;&#69;&#53;&#37;&#65;&#68;&#37;&#57;&#56;&#37;&#69;&#54;&#37;&#66;&#51;&#37;&#56;&#52;&#37;&#69;&#54;&#37;&#66;&#67;&#37;&#56;&#70;">brian@quiotix.com</a>), 首席顾问, Quiotix<br></p>
        <p>2005 年  12 月  19 日</p>
        <blockquote>虽
        然用 Java&#8482;
        语言编写的程序在理论上是不会出现&#8220;内存泄漏&#8221;的，但是有时对象在不再作为程序的逻辑状态的一部分之后仍然不被垃圾收集。本月，负责保障应用程序健康的工
        程师 Brian Goetz 探讨了无意识的对象保留的常见原因，并展示了如何用弱引用堵住泄漏。
        </blockquote><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters -->
        <!--end RESERVED FOR FUTURE USE INCLUDE FILES-->
        <p>
        要让垃圾收集（GC）回收程序不再使用的对象，对象的<em>逻辑</em> 生命周期（应用程序使用它的时间）和对该对象拥有的引用的<em>实际</em> 生命周期必须是相同的。在大多数时候，好的软件工程技术保证这是自动实现的，不用我们对对象生命周期问题花费过多心思。但是偶尔我们会创建一个引用，它在内存中包含对象的时间比我们预期的要长得多，这种情况称为<em>无意识的对象保留（unintentional object retention）</em>。
        </p>
        <p><a name="1.0">全局 Map 造成的内存泄漏</a></p>
        <p>
        无意识对象保留最常见的原因是使用 <code>Map</code> 将元数据与临时对象（transient object）相关联。假定一个对象具有中等生命周期，比分配它的那个方法调用的生命周期长，但是比应用程序的生命周期短，如客户机的套接字连接。需要将一些元数据与这个套接字关联，如生成连接的用户的标识。在创建 <code>Socket</code> 时是不知道这些信息的，并且不能将数据添加到 <code>Socket</code> 对象上，因为不能控制
        <code>Socket</code> 类或者它的子类。这时，典型的方法就是在一个全局
        <code>Map</code> 中存储这些信息，如清单 1 中的 <code>SocketManager</code> 类所示：
        </p>
        <br><a name="listing1"><strong>清单 1. 使用一个全局 Map 将元数据关联到一个对象</strong></a><br>
        <table border="0" cellpadding="0" cellspacing="0" width="100%">
            <tbody>
                <tr>
                    <td class="code-outline">
                    <pre class="displaycode">public class SocketManager {<br>    private Map&lt;Socket,User&gt; m = new HashMap&lt;Socket,User&gt;();<br>    <br>    public void setUser(Socket s, User u) {<br>        m.put(s, u);<br>    }<br>    public User getUser(Socket s) {<br>        return m.get(s);<br>    }<br>    public void removeUser(Socket s) {<br>        m.remove(s);<br>    }<br>}<br>SocketManager socketManager;<br>...<br>socketManager.setUser(socket, user);<br></pre>
                    </td>
                </tr>
            </tbody>
        </table>
        <br>
        <p>
        这种方法的问题是元数据的生命周期需要与套接字的生命周期挂钩，但是除非准确地知道什么时候程序不再需要这个套接字，并记住从 <code>Map</code> 中删除相应的映射，否则，<code>Socket</code> 和 <code>User</code>
        对象将会永远留在 <code>Map</code> 中，远远超过响应了请求和关闭套接字的时间。这会阻止 <code>Socket</code> 和 <code>User</code> 对象被垃圾收集，即使应用程序不会再使用它们。这些对象留下来不受控制，很容易造成程序在长时间运行后内存爆满。除了最简单的情况，在几乎所有情况下找出什么时候 <code>Socket</code> 不再被程序使用是一件很烦人和容易出错的任务，需要人工对内存进行管理。
        </p>
        <br>
        <table border="0" cellpadding="0" cellspacing="0" width="100%">
            <tbody>
                <tr>
                    <td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%"><br><img alt="" src="http://www.ibm.com/i/c.gif" border="0" height="6" width="8"></td>
                </tr>
            </tbody>
        </table>
        <table class="no-print" align="right" cellpadding="0" cellspacing="0">
            <tbody>
                <tr align="right">
                    <td><img src="http://www.ibm.com/i/c.gif" alt="" height="4" width="100%"><br>
                    <table border="0" cellpadding="0" cellspacing="0">
                        <tbody>
                            <tr>
                                <td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" height="16" width="16"><br></td>
                                <td align="right" valign="top"><a href="http://www.ibm.com/developerworks/cn/java/j-jtp11225/#main" class="fbox"><strong>回页首</strong></a></td>
                            </tr>
                        </tbody>
                    </table>
                    </td>
                </tr>
            </tbody>
        </table>
        <br><br>
        <p><a name="2.0">找出内存泄漏</a></p>
        <p>
        程序有内存泄漏的第一个迹象通常是它抛出一个 <code>OutOfMemoryError</code>，或者因为频繁的垃圾收集而表现出糟糕的性能。幸运的是，垃圾收集可以提供能够用来诊断内存泄漏的大量信息。如果以 <code>-verbose:gc</code> 或者 <code>-Xloggc</code>
        选项调用 JVM，那么每次 GC
        运行时在控制台上或者日志文件中会打印出一个诊断信息，包括它所花费的时间、当前堆使用情况以及恢复了多少内存。记录 GC
        使用情况并不具有干扰性，因此如果需要分析内存问题或者调优垃圾收集器，在生产环境中默认启用 GC 日志是值得的。
        </p>
        <p>
        有工具可以利用 GC 日志输出并以图形方式将它显示出来，JTune 就是这样的一种工具（请参阅 <a href="http://www.ibm.com/developerworks/cn/java/j-jtp11225/#resources">参考资料</a>）。观察 GC 之后堆大小的图，可以看到程序内存使用的趋势。对于大多数程序来说，可以将内存使用分为两部分：<em>baseline</em> 使用和 <em>current load</em>
        使用。对于服务器应用程序，baseline 使用就是应用程序在没有任何负荷、但是已经准备好接受请求时的内存使用，current load
        使用是在处理请求过程中使用的、但是在请求处理完成后会释放的内存。只要负荷大体上是恒定的，应用程序通常会很快达到一个稳定的内存使用水平。如果在应用
        程序已经完成了其初始化并且负荷没有增加的情况下，内存使用持续增加，那么程序就可能在处理前面的请求时保留了生成的对象。
        </p>
        <p>
        清单 2 展示了一个有内存泄漏的程序。<code>MapLeaker</code> 在线程池中处理任务，并在一个 <code>Map</code> 中记录每一项任务的状态。不幸的是，在任务完成后它不会删除那一项，因此状态项和任务对象（以及它们的内部状态）会不断地积累。
        </p>
        <br><a name="listing2"><strong>清单 2. 具有基于 Map 的内存泄漏的程序</strong></a><br>
        <table border="0" cellpadding="0" cellspacing="0" width="100%">
            <tbody>
                <tr>
                    <td class="code-outline">
                    <pre class="displaycode">public class MapLeaker {<br>    public ExecutorService exec = Executors.newFixedThreadPool(5);<br>    public Map&lt;Task, TaskStatus&gt; taskStatus <br>        = Collections.synchronizedMap(new HashMap&lt;Task, TaskStatus&gt;());<br>    private Random random = new Random();<br>    private enum TaskStatus { NOT_STARTED, STARTED, FINISHED };<br>    private class Task implements Runnable {<br>        private int[] numbers = new int[random.nextInt(200)];<br>        public void run() {<br>            int[] temp = new int[random.nextInt(10000)];<br>            taskStatus.put(this, TaskStatus.STARTED);<br>            doSomeWork();<br>            taskStatus.put(this, TaskStatus.FINISHED);<br>        }<br>    }<br>    public Task newTask() {<br>        Task t = new Task();<br>        taskStatus.put(t, TaskStatus.NOT_STARTED);<br>        exec.execute(t);<br>        return t;<br>    }<br>}<br></pre>
                    </td>
                </tr>
            </tbody>
        </table>
        <br>
        <p>
        图 1 显示 <code>MapLeaker</code> GC 之后应用程序堆大小随着时间的变化图。上升趋势是存在内存泄漏的警示信号。（在真实的应用程序中，坡度不会这么大，但是在收集了足够长时间的 GC 数据后，上升趋势通常会表现得很明显。）
        </p>
        <br><a name="figure1"><strong>图 1. 持续上升的内存使用趋势</strong></a><br>
        <img alt="" src="http://www.ibm.com/developerworks/cn/java/j-jtp11225/heapsize.gif">
        <br>
        <p>
        确信有了内存泄漏后，下一步就是找出哪种对象造成了这个问题。所有内存分析器都可以生成按照对象类进行分解的堆快照。有一些很好的商业堆分析工具，但是找出内存泄漏不一定要花钱买这些工具 —— 内置的
        <code>hprof</code> 工具也可完成这项工作。要使用
        <code>hprof</code> 并让它跟踪内存使用，需要以 <code>-Xrunhprof:heap=sites</code> 选项调用 JVM。
        </p>
        <p>
        清单 3 显示分解了应用程序内存使用的 <code>hprof</code> 输出的相关部分。（<code>hprof</code>
        工具在应用程序退出时，或者用 <code>kill -3</code> 或在 Windows 中按 Ctrl+Break 时生成使用分解。）注意两次快照相比，<code>Map.Entry</code>、<code>Task</code> 和 <code>int[]</code> 对象有了显著增加。
        </p>
        <p>请参阅 <a href="http://www.ibm.com/developerworks/cn/java/j-jtp11225/sidefile1.html"> 清单 3</a>。</p>
        <p>
        清单 4 展示了 <code>hprof</code> 输出的另一部分，给出了
        <code>Map.Entry</code> 对象的分配点的调用堆栈信息。这个输出告诉我们哪些调用链生成了 <code>Map.Entry</code> 对象，并带有一些程序分析，找出内存泄漏来源一般来说是相当容易的。
        </p>
        <br><a name="listing4"><strong>清单 4. HPROF 输出，显示 Map.Entry 对象的分配点</strong></a><br>
        <table border="0" cellpadding="0" cellspacing="0" width="100%">
            <tbody>
                <tr>
                    <td class="code-outline">
                    <pre class="displaycode">TRACE 300446:<br>	java.util.HashMap$Entry.&lt;init&gt;(&lt;Unknown Source&gt;:Unknown line)<br>	java.util.HashMap.addEntry(&lt;Unknown Source&gt;:Unknown line)<br>	java.util.HashMap.put(&lt;Unknown Source&gt;:Unknown line)<br>	java.util.Collections$SynchronizedMap.put(&lt;Unknown Source&gt;:Unknown line)<br>	com.quiotix.dummy.MapLeaker.newTask(MapLeaker.java:48)<br>	com.quiotix.dummy.MapLeaker.main(MapLeaker.java:64)<br></pre>
                    </td>
                </tr>
            </tbody>
        </table>
        <br>
        <br>
        <table border="0" cellpadding="0" cellspacing="0" width="100%">
            <tbody>
                <tr>
                    <td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%"><br><img alt="" src="http://www.ibm.com/i/c.gif" border="0" height="6" width="8"></td>
                </tr>
            </tbody>
        </table>
        <table class="no-print" align="right" cellpadding="0" cellspacing="0">
            <tbody>
                <tr align="right">
                    <td><img src="http://www.ibm.com/i/c.gif" alt="" height="4" width="100%"><br>
                    <table border="0" cellpadding="0" cellspacing="0">
                        <tbody>
                            <tr>
                                <td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" height="16" width="16"><br></td>
                                <td align="right" valign="top"><a href="http://www.ibm.com/developerworks/cn/java/j-jtp11225/#main" class="fbox"><strong>回页首</strong></a></td>
                            </tr>
                        </tbody>
                    </table>
                    </td>
                </tr>
            </tbody>
        </table>
        <br><br>
        <p><a name="3.0">弱引用来救援了</a></p>
        <p>
        <code>SocketManager</code> 的问题是 <code>Socket</code>-<code>User</code> 映射的生命周期应当与 <code>Socket</code> 的生命周期相匹配，但是语言没有提供任何容易的方法实施这项规则。这使得程序不得不使用人工内存管理的老技术。幸运的是，从 JDK 1.2 开始，垃圾收集器提供了一种声明这种对象生命周期依赖性的方法，这样垃圾收集器就可以帮助我们防止这种内存泄漏 —— 利用<em>弱引用</em>。
        </p>
        <p>
        弱引用是对一个对象（称为 <em>referent</em>）的引用的持有者。使用弱引用后，可以维持对 referent
        的引用，而不会阻止它被垃圾收集。当垃圾收集器跟踪堆的时候，如果对一个对象的引用只有弱引用，那么这个 referent
        就会成为垃圾收集的候选对象，就像没有任何剩余的引用一样，而且所有剩余的弱引用都被<em>清除</em>。（只有弱引用的对象称为<em>弱可及（weakly reachable）</em>。）
        </p>
        <p>
        <code>WeakReference</code> 的 referent 是在构造时设置的，在没有被清除之前，可以用
        <code>get()</code> 获取它的值。如果弱引用被清除了（不管是 referent 已经被垃圾收集了，还是有人调用了 <code>WeakReference.clear()</code>），<code>get()</code>
        会返回 <code>null</code>。相应地，在使用其结果之前，应当总是检查
        <code>get()</code> 是否返回一个非 null 值，因为 referent 最终总是会被垃圾收集的。
        </p>
        <p>
        用一个普通的（强）引用拷贝一个对象引用时，限制 referent
        的生命周期至少与被拷贝的引用的生命周期一样长。如果不小心，那么它可能就与程序的生命周期一样 ——
        如果将一个对象放入一个全局集合中的话。另一方面，在创建对一个对象的弱引用时，完全没有扩展 referent 的生命周期，只是在<em>对象仍然存活的时候</em>，保持另一种到达它的方法。
        </p>
        <p>
        弱引用对于构造弱集合最有用，如那些在应用程序的其余部分使用对象期间存储关于这些对象的元数据的集合 —— 这就是
        <code>SocketManager</code> 类所要做的工作。因为这是弱引用最常见的用法，<code>WeakHashMap</code> 也被添加到 JDK 1.2 的类库中，它对键（而不是对值）使用弱引用。如果在一个普通 <code>HashMap</code> 中用一个对象作为键，那么这个对象在映射从 <code>Map</code> 中删除之前不能被回收，<code>WeakHashMap</code> 使您可以用一个对象作为
        <code>Map</code> 键，同时不会阻止这个对象被垃圾收集。清单 5 给出了 <code>WeakHashMap</code> 的 <code>get()</code> 方法的一种可能实现，它展示了弱引用的使用：
        </p>
        <br><a name="listing5"><strong>清单 5. WeakReference.get() 的一种可能实现</strong></a><br>
        <table border="0" cellpadding="0" cellspacing="0" width="100%">
            <tbody>
                <tr>
                    <td class="code-outline">
                    <pre class="displaycode">public class WeakHashMap&lt;K,V&gt; implements Map&lt;K,V&gt; {<br>    private static class Entry&lt;K,V&gt; extends WeakReference&lt;K&gt; <br>      implements Map.Entry&lt;K,V&gt; {<br>        private V value;<br>        private final int hash;<br>        private Entry&lt;K,V&gt; next;<br>        ...<br>    }<br>    public V get(Object key) {<br>        int hash = getHash(key);<br>        Entry&lt;K,V&gt; e = getChain(hash);<br>        while (e != null) {<br>            K eKey= e.get();<br>            if (e.hash == hash &amp;&amp; (key == eKey || key.equals(eKey)))<br>                return e.value;<br>            e = e.next;<br>        }<br>        return null;<br>    }<br></pre>
                    </td>
                </tr>
            </tbody>
        </table>
        <br>
        <p>
        调用 <code>WeakReference.get()</code> 时，它返回一个对 referent 的强引用（如果它仍然存活的话），因此不需要担心映射在 <code>while</code> 循环体中消失，因为强引用会防止它被垃圾收集。<code>WeakHashMap</code> 的实现展示了弱引用的一种常见用法 —— 一些内部对象扩展 <code>WeakReference</code>。其原因在下面一节讨论引用队列时会得到解释。</p>
        <p>
        在向 <code>WeakHashMap</code> 中添加映射时，请记住映射可能会在以后&#8220;脱离&#8221;，因为键被垃圾收集了。在这种情况下，<code>get()</code> 返回
        <code>null</code>，这使得测试 <code>get()</code> 的返回值是否为 <code>null</code> 变得比平时更重要了。
        </p>
        <p><a name="3.1">用 WeakHashMap 堵住泄漏</a></p>
        <p>
        在 <code>SocketManager</code> 中防止泄漏很容易，只要用 <code>WeakHashMap</code> 代替 <code>HashMap</code> 就行了，如清单 6 所示。（如果 <code>SocketManager</code> 需要线程安全，那么可以用 <code>Collections.synchronizedMap()</code> 包装 <code>WeakHashMap</code>）。当映射的生命周期必须与键的生命周期联系在一起时，可以使用这种方法。不过，应当小心不滥用这种技术，大多数时候还是应当使用普通的 <code>HashMap</code> 作为
        <code>Map</code> 的实现。
        </p>
        <br><a name="listing6"><strong>清单 6. 用 WeakHashMap 修复 SocketManager</strong></a><br>
        <table border="0" cellpadding="0" cellspacing="0" width="100%">
            <tbody>
                <tr>
                    <td class="code-outline">
                    <pre class="displaycode">public class SocketManager {<br>    private Map&lt;Socket,User&gt; m = new WeakHashMap&lt;Socket,User&gt;();<br>    <br>    public void setUser(Socket s, User u) {<br>        m.put(s, u);<br>    }<br>    public User getUser(Socket s) {<br>        return m.get(s);<br>    }<br>}<br></pre>
                    </td>
                </tr>
            </tbody>
        </table>
        <br>
        <p><a name="3.2">引用队列</a></p>
        <p>
        <code>WeakHashMap</code> 用弱引用承载映射键，这使得应用程序不再使用键对象时它们可以被垃圾收集，<code>get()</code>
        实现可以根据
        <code>WeakReference.get()</code> 是否返回 <code>null</code> 来区分死的映射和活的映射。但是这只是防止 <code>Map</code> 的内存消耗在应用程序的生命周期中不断增加所需要做的工作的一半，还需要做一些工作以便在键对象被收集后从 <code>Map</code> 中删除死项。否则，<code>Map</code> 会充满对应于死键的项。虽然这对于应用程序是不可见的，但是它仍然会造成应用程序耗尽内存，因为即使键被收集了，<code>Map.Entry</code> 和值对象也不会被收集。
        </p>
        <p>
        可以通过周期性地扫描 <code>Map</code>，对每一个弱引用调用 <code>get()</code>，并在 get() 返回 <code>null</code> 时删除那个映射而消除死映射。但是如果 <code>Map</code> 有许多活的项，那么这种方法的效率很低。如果有一种方法可以在弱引用的 referent 被垃圾收集时发出通知就好了，这就是<em>引用队列</em> 的作用。
        </p>
        <p>
        引用队列是垃圾收集器向应用程序返回关于对象生命周期的信息的主要方法。弱引用有两个构造函数：一个只取 referent
        作为参数，另一个还取引用队列作为参数。如果用关联的引用队列创建弱引用，在 referent 成为 GC 候选对象时，这个引用对象（不是
        referent）就在引用清除后<em>加入</em> 到引用队列中。之后，应用程序从引用队列提取引用并了解到它的 referent 已被收集，因此可以进行相应的清理活动，如去掉已不在弱集合中的对象的项。（引用队列提供了与
        <code>BlockingQueue</code> 同样的出列模式 —— polled、timed blocking 和 untimed
        blocking。）
        </p>
        <p>
        <code>WeakHashMap</code> 有一个名为
        <code>expungeStaleEntries()</code> 的私有方法，大多数
        <code>Map</code> 操作中会调用它，它去掉引用队列中所有失效的引用，并删除关联的映射。清单 7 展示了 <code>expungeStaleEntries()</code> 的一种可能实现。用于存储键-值映射的 <code>Entry</code> 类型扩展了 <code>WeakReference</code>，因此当
        <code>expungeStaleEntries()</code> 要求下一个失效的弱引用时，它得到一个 <code>Entry</code>。用引用队列代替定期扫描内容的方法来清理 <code>Map</code> 更有效，因为清理过程不会触及活的项，只有在有实际加入队列的引用时它才工作。
        </p>
        <br><a name="listing7"><strong>清单 7. WeakHashMap.expungeStaleEntries()  的可能实现</strong></a><br>
        <table border="0" cellpadding="0" cellspacing="0" width="100%">
            <tbody>
                <tr>
                    <td class="code-outline">
                    <pre class="displaycode">    private void expungeStaleEntries() {<br>	Entry&lt;K,V&gt; e;<br>        while ( (e = (Entry&lt;K,V&gt;) queue.poll()) != null) {<br>            int hash = e.hash;<br>            Entry&lt;K,V&gt; prev = getChain(hash);<br>            Entry&lt;K,V&gt; cur = prev;<br>            while (cur != null) {<br>                Entry&lt;K,V&gt; next = cur.next;<br>                if (cur == e) {<br>                    if (prev == e)<br>                        setChain(hash, next);<br>                    else<br>                        prev.next = next;<br>                    break;<br>                }<br>                prev = cur;<br>                cur = next;<br>            }<br>        }<br>    }<br></pre>
                    </td>
                </tr>
            </tbody>
        </table>
        <br>
        <br>
        <table border="0" cellpadding="0" cellspacing="0" width="100%">
            <tbody>
                <tr>
                    <td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%"><br><img alt="" src="http://www.ibm.com/i/c.gif" border="0" height="6" width="8"></td>
                </tr>
            </tbody>
        </table>
        <table class="no-print" align="right" cellpadding="0" cellspacing="0">
            <tbody>
                <tr align="right">
                    <td><img src="http://www.ibm.com/i/c.gif" alt="" height="4" width="100%"><br>
                    <table border="0" cellpadding="0" cellspacing="0">
                        <tbody>
                            <tr>
                                <td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" height="16" width="16"><br></td>
                                <td align="right" valign="top"><a href="http://www.ibm.com/developerworks/cn/java/j-jtp11225/#main" class="fbox"><strong>回页首</strong></a></td>
                            </tr>
                        </tbody>
                    </table>
                    </td>
                </tr>
            </tbody>
        </table>
        <br><br>
        <p><a name="4.0">结束语</a></p>
        <p>
        弱引用和弱集合是对堆进行管理的强大工具，使得应用程序可以使用更复杂的可及性方案，而不只是由普通（强）引用所提供的&#8220;要么全部要么没有&#8221;可及性。下个月，我们将分析与弱引用有关的<em>软引用</em>，将分析在使用弱引用和软引用时，垃圾收集器的行为。
        </p>
        <br><br>
        <p><a name="resources">参考资料 </a></p>
        <strong>学习</strong><br>
        <ul>
            <li>您可以参阅本文在 developerWorks 全球站点上的
            <a href="http://www.ibm.com/developerworks/java/library/j-jtp11225/?S_TACT=105AGX52&amp;S_CMP=cn-a-j" target="_blank">英文原文</a>
            <br><br></li>
            <li>
            &#8220;<a href="http://www.ibm.com/developerworks/cn/java/j-perf06304/">关注性能: 调优垃圾收集</a>&#8221;：Kirk Pepperdine 和 Jack
            Shirazi 展示了缓慢的内存泄漏最终对于垃圾收集器造成了无法承受的压力。<br><br></li>
            <li>
            &#8220;<a href="http://java.sun.com/developer/technicalArticles/Programming/HPROF.html">HPROF</a>&#8221;：Sun 的这一篇文章描述了如何使用内置的 HPROF 分析工具。<br><br></li>
            <li>
            <a href="http://java.sun.com/developer/technicalArticles/ALT/RefObj/">Reference
            objects and garbage collection</a>：Sun 的这篇文章是在 Reference 对象刚加入类库不久写的，描述了垃圾收集器如何处理 Reference 对象。<br><br></li>
            <li><a href="http://www.ibm.com/developerworks/cn/views/java/articles.jsp?view_by=search&amp;search_by=Java+%E7%90%86%E8%AE%BA%E4%B8%8E%E5%AE%9E%E8%B7%B5%EF%BC%9A"><em>Java 理论与实践</em></a>：Brian Goetz 所写的全部系列文章。<br><br></li>
            <li><a href="http://www.ibm.com/developerworks/cn/java/">Java 技术专区</a>：数百篇关于 Java 编程各个方面的文章。<br><br></li>
        </ul>
        <br><strong>获得产品和技术</strong><br>
        <ul>
            <li>
            <a href="http://www.hp.com/products1/unix/java/java2/hpjtune/">JTune</a>：免费 JTune 工具，可以使用 GC 日志并以图形方式显示堆大小、GC 持续时间和其他有用的内存管理数据。<br><br></li>
        </ul>
        <br><strong>讨论</strong><br>
        <ul>
            <li><a href="http://www.ibm.com/developerworks/community/">参与论坛讨论</a>。<br><br></li>
            <li> <a href="http://www.ibm.com/developerworks/blogs/?S_TACT=105AGX52&amp;S_CMP=cn-a-j">developerWorks blogs</a>：参与 developerWorks 社区。</li>
        </ul>
        <br><br>
        <p><a name="author">关于作者</a></p>
        <table border="0" cellpadding="0" cellspacing="0" width="100%">
            <tbody>
                <tr>
                    <td colspan="3"><img alt="" src="http://www.ibm.com/i/c.gif" height="5" width="100%"></td>
                </tr>
                <tr align="left" valign="top">
                    <td><br></td>
                    <td><img alt="" src="http://www.ibm.com/i/c.gif" height="5" width="4"></td>
                    <td width="100%">
                    <p><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#98;&#114;&#105;&#97;&#110;&#64;&#113;&#117;&#105;&#111;&#116;&#105;&#120;&#46;&#99;&#111;&#109;&#63;&#99;&#99;&#61;">Brian Goetz</a> 成为专业软件开发人员已经超过 18 年了。他是 Quiotix 的首席顾问，该公司是位于加利福尼亚 Los Altos 的软件开发和咨询公司。他参加了几个 JCP 专家组。Brian 的 <a href="http://www.amazon.com/exec/obidos/ASIN/0321349601/ref=nosim/none0b69"><em>Java Concurrency In Practice</em></a> 一书将于 2005 年末由 Addison-Wesley 出版。请在业界流行的出版物上查阅 Brian <a href="http://www.briangoetz.com/pubs.html">已发表的和即将发表的文章</a>。
                    </p>
                    </td>
                </tr>
            </tbody>
        </table>
        </td>
    </tr>
</tbody>
</table>
<br><img src ="http://www.blogjava.net/sutao/aggbug/138865.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sutao/" target="_blank">苏醄</a> 2007-08-23 15:56 <a href="http://www.blogjava.net/sutao/articles/138865.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开发大型高负载类网站应用的几个要点  </title><link>http://www.blogjava.net/sutao/articles/138828.html</link><dc:creator>苏醄</dc:creator><author>苏醄</author><pubDate>Thu, 23 Aug 2007 06:30:00 GMT</pubDate><guid>http://www.blogjava.net/sutao/articles/138828.html</guid><wfw:comment>http://www.blogjava.net/sutao/comments/138828.html</wfw:comment><comments>http://www.blogjava.net/sutao/articles/138828.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sutao/comments/commentRss/138828.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sutao/services/trackbacks/138828.html</trackback:ping><description><![CDATA[<a style="color: #3c20ff;" href="http://www.searchfull.net/blog/2007/08/04/1186194548846.html" name="a1186194548846" title="http://www.searchfull.net:80/blog/2007/08/04/1186194548846.html">开发大型高负载类网站应用的几个要点</a><span style="color: #3c20ff;">
</span><br style="color: #3c20ff;">
<p style="color: #3c20ff;">看了一些人的所谓大型项目的方法,我感觉都是没有说到点子上，有点难受。<br>
我也说说自己的看法.我个人认为,很难衡量所谓项目是否大型,<br>
即便很简单的应用在高负载和高增长情况下都是一个挑战.因此,按照我的想法,姑且说是高负载<br>
高并发或者高增长情况下,需要考虑的问题.这些问题,很多是和程序开发无关,而是和整个系统的<br>
架构密切相关的.<br>
<br>
</p>
<ul style="color: #3c20ff;">
    <li>数据库</li>
</ul>
<p style="color: #3c20ff;">没错,首先是数据库,这是大多数应用所面临的首个SPOF。尤其是Web2.0的应用，数据库的响应是首先要解决的。<br>
一般来说MySQL是最常用的，可能最初是一个mysql主机，当数据增加到100万以上，<br>
那么，MySQL的效能急剧下降。常用的优化措施是M-S（主-从）方式进行同步复制，将查询和操作和分别在不同的<br>
服务器上进行操作。我推荐的是M-M-Slaves方式，2个主Mysql，多个Slaves，需要注意的是，虽然有2个Master，<br>
但是同时只有1个是Active，我们可以在一定时候切换。之所以用2个M，是保证M不会又成为系统的SPOF。<br>
Slaves可以进一步负载均衡，可以结合LVS,从而将select操作适当的平衡到不同的slaves上。<br>
<br>
以上架构可以抗衡到一定量的负载，但是随着用户进一步增加，你的用户表数据超过1千万，这时那个M变成了<br>
SPOF。你不能任意扩充Slaves，否则复制同步的开销将直线上升，怎么办？我的方法是表分区，<br>
从业务层面上进行分区。最简单的，以用户数据为例。根据一定的切分方式，比如id，切分到不同的数据库集群去。<br>
全局数据库用于meta数据的查询。缺点是每次查询，会增加一次，比如你要查一个用户nightsailer,你首先要到<br>
全局数据库群找到nightsailer对应的cluster id，然后再到指定的cluster找到nightsailer的实际数据。<br>
每个cluster可以用m-m方式，或者m-m-slaves方式。<br>
这是一个可以扩展的结构，随着负载的增加，你可以简单的增加新的mysql cluster进去。<br>
<br>
需要注意的是：<br>
1、禁用全部auto_increment的字段<br>
2、id需要采用通用的算法集中分配<br>
3、要具有比较好的方法来监控mysql主机的负载和服务的运行状态。如果你有30台以上的mysql数据库在跑就明白我的意思了。<br>
4、不要使用持久性链接（不要用pconnect）,相反，使用sqlrelay这种第三方的数据库链接池，或者干脆自己做，因为php4中mysql的<br>
链接池经常出问题。<br>
</p>
<ul style="color: #3c20ff;">
    <li>缓存</li>
</ul>
<p style="color: #3c20ff;">缓存是另一个大问题，我一般用memcached来做缓存集群，一般来说部署10台左右就差不多（10g内存池）。需要注意一点，千万不能用使用<br>
swap，最好关闭linux的swap。<br>
</p>
<ul style="color: #3c20ff;">
    <li>负载均衡/加速</li>
</ul>
<p style="color: #3c20ff;">可能上面说缓存的时候，有人第一想的是页面静态化，所谓的静态html，我认为这是常识，不属于要点了。页面的静态化随之带来的是静态服务的<br>
负载均衡和加速。我认为Lighttped+Squid是最好的方式了。<br>
LVS &lt;-------&gt;lighttped====&gt;squid(s) ====lighttpd<br>
<br>
上面是我经常用的。注意，我没有用apache，除非特定的需求，否则我不部署apache，因为我一般用php-fastcgi配合lighttpd,<br>
性能比apache+mod_php要强很多。<br>
<br>
squid的使用可以解决文件的同步等等问题，但是需要注意，你要很好的监控缓存的命中率，尽可能的提高的90%以上。<br>
squid和lighttped也有很多的话题要讨论，这里不赘述。<br>
</p>
<ul style="color: #3c20ff;">
    <li>存储</li>
</ul>
<p style="color: #3c20ff;">存储也是一个大问题，一种是小文件的存储，比如图片这类。另一种是大文件的存储，比如搜索引擎的索引，一般单文件都超过2g以上。<br>
小文件的存储最简单的方法是结合lighttpd来进行分布。或者干脆使用Redhat的GFS，优点是应用透明，缺点是费用较高。我是指<br>
你购买盘阵的问题。我的项目中，存储量是2-10Tb，我采用了分布式存储。这里要解决文件的复制和冗余。<br>
这样每个文件有不同的冗余，这方面可以参考google的gfs的论文。<br>
大文件的存储，可以参考nutch的方案，现在已经独立为hadoop子项目。(你可以google it)<br>
<br>
其他：<br>
此外，passport等也是考虑的，不过都属于比较简单的了。</p>
<br style="color: #3c20ff;"><img src ="http://www.blogjava.net/sutao/aggbug/138828.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sutao/" target="_blank">苏醄</a> 2007-08-23 14:30 <a href="http://www.blogjava.net/sutao/articles/138828.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>随即函数</title><link>http://www.blogjava.net/sutao/articles/138627.html</link><dc:creator>苏醄</dc:creator><author>苏醄</author><pubDate>Wed, 22 Aug 2007 07:36:00 GMT</pubDate><guid>http://www.blogjava.net/sutao/articles/138627.html</guid><wfw:comment>http://www.blogjava.net/sutao/comments/138627.html</wfw:comment><comments>http://www.blogjava.net/sutao/articles/138627.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sutao/comments/commentRss/138627.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sutao/services/trackbacks/138627.html</trackback:ping><description><![CDATA[<span style="color: #2e10ff;"></span>
<table style="color: #2e10ff;" align="center" border="0" cellpadding="4" cellspacing="0" width="99%">
    <tbody>
        <tr>
            <td><strong> Java随机数类Random介绍</strong> &nbsp;&nbsp;&nbsp;</td>
            <td align="right"><a href="http://post.blog.hexun.com/redlly/postarticle.aspx?articleid=2545565" title="编辑..."> <img src="http://blog.hexun.com/images/icon_edit.gif" border="0"></a> <a href="http://blog.hexun.com/redlly/deletearticle.aspx?articleid=2545565" title="删除..."><img src="http://blog.hexun.com/images/icon_del.gif" border="0"></a> </td>
        </tr>
        <tr>
            <td colspan="2">
            <p>
            Java实用工具类库中的类java.util.Random提供了产生各种类型随机数的方法。它可以产生int、long、float、double以
            及Goussian等类型的随机数。这也是它与java.lang.Math中的方法Random()最大的不同之处，后者只产生double型的随机
            数。<br>
            类Random中的方法十分简单，它只有两个构造方法和六个普通方法。<br>
            构造方法：<br>
            (1)public Random()<br>
            (2)public Random(long seed)<br>
            Java产生随机数需要有一个基值seed，在第一种方法中基值缺省，则将系统时间作为seed。<br>
            普通方法：<br>
            (1)public synonronized void setSeed(long seed)<br>
            该方法是设定基值seed。<br>
            (2)public int nextInt()<br>
            该方法是产生一个整型随机数。<br>
            (3)public long nextLong()<br>
            该方法是产生一个long型随机数。<br>
            (4)public float nextFloat()<br>
            该方法是产生一个Float型随机数。<br>
            (5)public double nextDouble()<br>
            该方法是产生一个Double型随机数。<br>
            (6)public synchronized double nextGoussian()<br>
            该方法是产生一个double型的Goussian随机数。<br>
            例2 RandomApp.java。<br>
            //import java.lang.*;<br>
            import java.util.Random;<br>
            <br>
            public class RandomApp{<br>
            public static void main(String args[]){<br>
            Random ran1=new Random();<br>
            Random ran2=new Random(12345);<br>
            //创建了两个类Random的对象。<br>
            System.out.println("The 1st set of random numbers:");<br>
            System.out.println("  Integer:"+ran1.nextInt());<br>
            System.out.println("  Long:"+ran1.nextLong());<br>
            System.out.println("  Float:"+ran1.nextFloat());<br>
            System.out.println("  Double:"+ran1.nextDouble());<br>
            System.out.println("  Gaussian:"+ran1.nextGaussian());<br>
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  //产生各种类型的随机数<br>
            System.out.print("The 2nd set of random numbers:");<br>
            for(int i=0;i&lt;5;i++){<br>
            System.out.println(ran2.nextInt()+" ");<br>
            if(i==2) System.out.println();<br>
            //产生同种类型的不同的随机数。<br>
            System.out.println();<br>
            }<br>
            }<br>
            }</p>
            </td>
        </tr>
    </tbody>
</table>
<p style="color: #2e10ff;">Random random=new Random();<br>
random.nextInt();<br>
<br>
也可以有nextFloat等等,各种基本类型都有</p>
<div style="color: #2e10ff;" class="f14 wr">Math.random也可以<br>
比如说你想要0-10之间的随机数<br>
你可以这样写<br>
(int)(Math.random()*10);</div>
<p style="color: #2e10ff;">JAVA产生指定范围的随机数》</p>
<p style="color: #2e10ff;">《JAVA产生指定范围的随机数》<br>
&nbsp;&nbsp; 产生机制： <br>
产生Min-Max之间的数字<br>
&nbsp;&nbsp; 实现原理：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Math.round(Math.random()*(Max-Min)+Min)</p>
<span style="color: #2e10ff;">
long Temp; //不能设定为int,必须设定为long</span><br style="color: #2e10ff;"><span style="color: #2e10ff;">
//产生1000到9999的随机数</span><br style="color: #2e10ff;"><span style="color: #2e10ff;">
Temp=Math.round(Math.random()*8999+1000);
</span><br><img src ="http://www.blogjava.net/sutao/aggbug/138627.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sutao/" target="_blank">苏醄</a> 2007-08-22 15:36 <a href="http://www.blogjava.net/sutao/articles/138627.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>jdbc批量插入数据出错</title><link>http://www.blogjava.net/sutao/articles/138626.html</link><dc:creator>苏醄</dc:creator><author>苏醄</author><pubDate>Wed, 22 Aug 2007 07:34:00 GMT</pubDate><guid>http://www.blogjava.net/sutao/articles/138626.html</guid><wfw:comment>http://www.blogjava.net/sutao/comments/138626.html</wfw:comment><comments>http://www.blogjava.net/sutao/articles/138626.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sutao/comments/commentRss/138626.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sutao/services/trackbacks/138626.html</trackback:ping><description><![CDATA[<h2>Recommendations</h2>
<p>Modify the following two values in the Windows registry:</p>
<p>This one modifies the range of ports that Windows uses to open
connections. By default it only allows up to the port 5000. By
modifying this value, Windows will be able to open up more ports before
having to recycle back to the beginning. Every connection uses a port,
so it starts at 1025 and goes up to this value. When it reaches the max
value it goes back to 1025 and tries to open up that port again.</p>
<p>System Key: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters<br>
Name: MaxUserPort<br>
Type: REG_DWORD<br>
Value: 5000-65534</p>
<p>This will "release" closed ports faster. By default Windows leaves a
port in a TIME_WAIT state for 240 seconds. This can cause problems if
the MaxPort value is set to where a new connection will use an "older"
port that has not been removed from TIME_WAIT state yet. By decreasing
this value, you allow the connections to be released faster.</p>
<p>System Key: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters<br>
Value Name: TcpTimedWaitDelay<br>
Data Type: REG_DWORD<br>
Value Data: 30-300 </p>
<br><img src ="http://www.blogjava.net/sutao/aggbug/138626.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sutao/" target="_blank">苏醄</a> 2007-08-22 15:34 <a href="http://www.blogjava.net/sutao/articles/138626.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java学习网站</title><link>http://www.blogjava.net/sutao/articles/138503.html</link><dc:creator>苏醄</dc:creator><author>苏醄</author><pubDate>Wed, 22 Aug 2007 01:01:00 GMT</pubDate><guid>http://www.blogjava.net/sutao/articles/138503.html</guid><wfw:comment>http://www.blogjava.net/sutao/comments/138503.html</wfw:comment><comments>http://www.blogjava.net/sutao/articles/138503.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sutao/comments/commentRss/138503.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sutao/services/trackbacks/138503.html</trackback:ping><description><![CDATA[英文网站 <br>http://www.javaalmanac.com - Java开发者年鉴一书的在线版本. 要想快速查到某种Java技巧的用法及示例代码, 这是一个不错的去处. <br>http://www.onjava.com - O'Reilly的Java网站. 每周都有新文章. <br>http://java.sun.com - 官方的Java开发者网站 - 每周都有新文章发表. <br>http://www.developer.com/java - 由Gamelan.com 维护的Java技术文章网站. <br>http://www.java.net - Sun公司维护的一个Java社区网站. <br>http://www.builder.com - Cnet的Builder.com网站 - 所有的技术文章, 以Java为主. <br>http://www.ibm.com/developerworks/java - IBM的Developerworks技术网站; 这是其中的Java技术主页. <br>http://www.javaworld.com - 最早的一个Java站点. 每周更新Java技术文章. <br>http://www.devx.com/java - DevX维护的一个Java技术文章网站. <br>http://www.fawcette.com/javapro - JavaPro在线杂志网站. <br>http://www.sys-con.com/java - Java Developers Journal的在线杂志网站. <br>http://www.javadesktop.org - 位于Java.net的一个Java桌面技术社区网站. <br>http://www.theserverside.com - 这是一个讨论所有Java服务器端技术的网站. <br>http://www.jars.com - 提供Java评论服务. 包括各种framework和应用程序. <br>http://www.jguru.com - 一个非常棒的采用Q&amp;A形式的Java技术资源社区. <br>http://www.javaranch.com - 一个论坛，得到Java问题答案的地方，初学者的好去处。 <br>http://www.ibiblio.org/javafaq/javafaq.html - comp.lang.java的FAQ站点 - 收集了来自comp.lang.java新闻组的问题和答案的分类目录. <br>http://java.sun.com/docs/books/tutorial/ - 来自SUN公司的官方Java指南 - 对于了解几乎所有的java技术特性非常有帮助. <br>http://www.javablogs.com - 互联网上最活跃的一个Java Blog网站. <br>http://java.about.com/ - 来自About.com的Java新闻和技术文章网站. <br>http://www.objectlearn.com/index.jsp<br>中文网站<br>http://community.csdn.net/expert/forum.asp   CSDN技术社区<br>http://www.java360.cn/ Java开源世界-了解Java开源不得不去的地方<br>http://www-900.ibm.com/developerWorks/cn/java/index.shtml<br>http://diy.ccidnet.com/pub/article/c317_a71330_p1.html  赛迪网J2EE专题<br>http://www.javaresearch.org/    Java研究组织<br>http://www.jdon.com/   J道－Java和J2EE解决之道
<br><br>JAVA内部培训光盘资料<br>一、JAVA基础培训<br>1.孙鑫JAVA高级软件工程师班培训录像（2DVD)<br>   简介：光盘由孙鑫老师亲自授课录制。内容涵盖面广，从入门到精通，授课通俗易懂，分析问题独到精辟，学员通过本套光盘的学习，能够快速掌握Java编程语言，成为Java高手。循序渐进、通俗易懂、实战性强、内容全面、讲解深刻、问答形式是本套培训录像的特色。<br>2.张孝祥JAVA就业培训基础篇+高级篇(完整版)(4DVD) <br>
简介：张孝祥老师系列编程教学光盘，让您免除舟车劳顿。只要有一台计算机，就可用优惠的价格，成为精通相关领域知识，拥有丰富实践经验，能够独当一面的专
家！深入浅出的理论分析、精练生动的案例讲解、亲切直观的操作界面、恍然大悟的学习收获。张孝祥老师的课程，就不用多说了。<br>3.最新尚学堂JAVA系列培训（全）(推荐)（共18辑/300小讲 Avi格式，含源代码及PPT幻灯片笔记文件 1DVD）<br>   简介：JAVA基础全方位系统培训，真正手把手的视频培训录像。从本课程开始至结束，您将全面、深入 的学习到Java全系列的知识，并能够积累 2 年 左右的工作经验，您也能够学习到企业文化、沟通技巧、面试技巧等相关知识。 <br>二、J2ME移动通信开发方向培训<br>1.J2ME手机游戏开发（J2ME基础培训）（avi格式 2DVD）<br>
本培训主要讲解J2ME和游戏开发的基本知识，通过几个完整的游戏实例开发过程和实例分析告诉大家，在手机游戏开发中要注意哪些问题，会经常使用哪些技
巧。这个课程一共用到了12个大小不同的游戏开发实例，其中包括4个比较完整的手机游戏。这些实例不但具有趣味性，而且具有很强的针对性，我们能够通过它
们重点学习和掌握相应的技能。<br>2.J2ME移动开发与实例（J2ME高级培训）（avi格式 1DVD）<br>   通过本课程的学习，学员将具有J2ME开发的理论基础和实际设计经验，可以快速进入移动应用的领域，在企业中进行J2ME手机程序的实际开发工作。<br>3.J2ME专题系列培训（1DVD）<br>   ＊J2ME移动开发环境配置及如何使用eclipse开发J2ME应用程序实录<br>   ＊J2ME在诺基亚平台上应用开发讲座<br>   ＊针对J2ME的SIP和Web Services的应用开发讲座<br>   ＊J2ME（Eclipse+Tomcat+EclipseME）开发手机与互联网进行通信视频录像<br>   ＊魔塔游戏、连连看游戏、星座程序源代码（J2ME开发）<br>三、J2EE企业开发方向培训<br>1.JAVA-WEB开发实战（共6部分/25讲 Avi格式，含源代码及PDF幻灯片笔记文件 1DVD）<br>    突出了JAVA-WEB基础语法的讲解，从JSP的基础知识和基本语法入手，循序渐进、深入浅出地介绍了JSP的各种技术，并配以大量的实例和精选的代码，细致讲解JAVA-WEB动态网页编程的方法与技巧。<br>2.J2EE Web开发培训录像（共38辑/86课时 Rmvb格式 2DVD）<br>
本课程讲解的是J2EE之中应用最为广泛的WEB层技术，包括Servlet/JSP/JavaBean/JDBC/MVC/Struts框架等等技术，
本课程不仅仅讲解单个知识点，而更多的是考虑在实际应用中如何组合这些技术形成一个优秀的解决方案，更为贴近实际。由国内最知名的J2EE讲师执教，课程
生动形象并结合典型企业案例（网上银行、公文流转系统等）深入的分析。<br>3.Eclipse实战视频录像（共22讲，Wmv格式 1DVD）<br>    深入浅出的讲解了Eclipse的基本、高级操作，插件的安装使用以及jsp环境的搭建。详细讲解了Hibernate工具-CowNewStudio的使用和基于CowNewPIS的开发实战。<br>4.J2EE企业级软件工程师培训（含具体项目案例分析）（共3部分/37讲 Avi格式 2DVD）<br>
本培训覆盖J2EE开发的各个方面，从Web组件到EJB，从开发工具到应用服务器管理等；内容含盖Java初级，J2EE高级和系统架构等技术。提供经
典案例分析，结合理论课程，以现代软件工程的原则为指导，分析软件开发的全过程，包括需求分析，架构设计，详细设计，编码，模式设计，部署等环节，使学员
能够掌握J2EE软件设计和开发的最佳实践过程。<br>5.孙鑫J2EE培训录像（共76讲 WRF格式 1DVD）<br>
本视频侧重于J2EE框架机制原理，内容分为spring,struts,hibernate,powerdesign四个部分的讲解（Spring共
23讲，详细讲解了spring的使用。Struts共13讲，Struts通过一个完整视例讲解全部流程，以及标签库。Hibernate共24讲，深
入讲解了hibernate的机制。PowerDesigner共6讲，讲解数据库建模的步骤及其工具的使用。）<br>6.J2EE中间件技术视频录像（共24讲 CSF视频格式 1DVD）<br>  本培训涵盖了j2EE架构基础、Servlet/jsp/EJB开发，以及J2EE应用部署和J2EE应用开发架构。最后通过具体实例阐述，让你更清晰地掌握相关知识。<br>7.Struts技术实战录像（共31讲 Avi格式 1DVD）<br>  详细介绍了开发Struts应用的模型、视图和控制器的各种技术，细致的描述了Struts配置文件的每个元素的使用方法。引导受训者把握设计、开发和部署Struts应用的整体流程，充分体会Struts框架在开放大型、可扩展的Web应用方面发挥的优势。 <br>8.J2EE专题系列培训录像（2DVD）<br>   ＊JAVA UML（共6辑/17讲）<br>   ＊Hibernate（共四部分 Hibernate入门、Hibernate实体映射、Hibernate复合主键、Hibernate实体层设计 含PDF幻灯片笔记文件及源代码）<br>   ＊EJB专题培训（分别基于Eclipse和JBuilder开发的，配合了文本说明以及流程录像）（共16讲）<br>   ＊Struts、Spring、Hibernate、SSH专题培训（共16讲）<br>   ＊java高级JNI核心理论培训视频<br>9.项目培训专辑（含源代码及PPT幻灯片笔记文件 1DVD）<br>   ＊在线聊天系统项目实录<br>   ＊坦克大战游戏项目开发实录<br>   ＊JDBC_MySQL_BBS项目实战教程（教你详尽透彻理解JDBC/JSP/Servlet/HTML/CSS/JavaScript）<br>   ＊WebWork+Spring+Hibernate整合开发网络书城项目实录<br>四、数据库培训<br>1.Mysql数据库视频教程（共25讲 Rmvb格式 1DVD）<br>2.SQL Server 2000数据库系统管理与维护（共13部分 CSF格式 1DVD）<br>3.SQL Server 2005大型视频培训系列录像（微软多名认证专家  共52讲 Wmv格式 1DVD）<br>4.Oracle 9i DBA大型中文视频培训录像（附全套PPT幻灯片培训讲稿）（全、共13.4G 4DVD）<br>  1).课程设置    <br>    ＊1Z0-007 Introduction to Oracle9i SQL <br>    ＊1Z0-031 Oracle9i DBA Fundamentals I <br>    ＊1Z0-032 Oracle9i DBA Fundamentals II <br>    ＊1Z0-033 Oracle9i Performance Tuning   <br>  2).名师主讲　<br>      杨荣凯----微软大课堂深圳地区SQL讲师，中国电信深圳培训中心数据库讲师，教授数据库课程、数据库顾问。　  <br>      全程标准Oracle大学授课环境，资深Oracle认证讲师、中国电信深圳培训中心数据库讲师主讲。具有多年的Oracle DBA教学经验，授课风格自成一体，逻辑清晰、层次分明，讲解深入浅出、信息量大,实用性强,短时间内把您培养成高级数据库专家。<br>5.Oracle 10G 数据库培训录像（共18讲 1DVD）<br>6.IBM DB2数据库培训录像（共12集  Avi格式 1DVD）<br>7.计算机数据库方向前沿技术专题视频讲座（27讲 csf格式 1DVD）<br>   ＊01-04 GPS先时数据技术<br>   ＊05-10 大型数据库<br>   ＊11-20 集群主机应用<br>   ＊21-27 相关前沿技术<br>六、项目经理快速培训<br>    IT项目管理培训录像（项目经理提升必看）（共28集 Rmvb格式 1DVD）<br><br>另赠送如下任一套Linux操作系统<br>1.Linux从入门到精通培训录像（共32讲 1DVD）<br>2.Linux企业实战工程师培训录像（共8辑/75课时 Avi格式 1DVD）<br><br><br>需要的联系：<br>Q Q:65212235
<br><br><img src ="http://www.blogjava.net/sutao/aggbug/138503.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sutao/" target="_blank">苏醄</a> 2007-08-22 09:01 <a href="http://www.blogjava.net/sutao/articles/138503.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>hibernate实践</title><link>http://www.blogjava.net/sutao/articles/134600.html</link><dc:creator>苏醄</dc:creator><author>苏醄</author><pubDate>Mon, 06 Aug 2007 02:01:00 GMT</pubDate><guid>http://www.blogjava.net/sutao/articles/134600.html</guid><wfw:comment>http://www.blogjava.net/sutao/comments/134600.html</wfw:comment><comments>http://www.blogjava.net/sutao/articles/134600.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sutao/comments/commentRss/134600.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sutao/services/trackbacks/134600.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 一. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 序 在实际项目中使用Hibernate有两年多了，在两年多的实践过程中既体验到了Hibernate带来的N多好处，同时也碰到不少的问题，特写此篇文章做个总结，记录自己在Hibernate实践中的一些经验，希望对于新使用Hibernate的朋友能...&nbsp;&nbsp;<a href='http://www.blogjava.net/sutao/articles/134600.html'>阅读全文</a><img src ="http://www.blogjava.net/sutao/aggbug/134600.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sutao/" target="_blank">苏醄</a> 2007-08-06 10:01 <a href="http://www.blogjava.net/sutao/articles/134600.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>防范JAVA内存泄漏解决方案</title><link>http://www.blogjava.net/sutao/articles/133639.html</link><dc:creator>苏醄</dc:creator><author>苏醄</author><pubDate>Tue, 31 Jul 2007 10:35:00 GMT</pubDate><guid>http://www.blogjava.net/sutao/articles/133639.html</guid><wfw:comment>http://www.blogjava.net/sutao/comments/133639.html</wfw:comment><comments>http://www.blogjava.net/sutao/articles/133639.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sutao/comments/commentRss/133639.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sutao/services/trackbacks/133639.html</trackback:ping><description><![CDATA[<div class=ArcTitle>
<h3>防范JAVA内存泄漏解决方案</h3>
<div class=ArcDate>2007-04-12 12:41 &nbsp;&nbsp;&nbsp;&nbsp;推荐:2129&nbsp;&nbsp;&nbsp;&nbsp;收藏:1369&nbsp;&nbsp;&nbsp;&nbsp;评论:0&nbsp;&nbsp;&nbsp;&nbsp; 来源:<a style="TEXT-DECORATION: none" href="http://www.it168.com/" target=_blank><span style="COLOR: rgb(0,0,0)">IT168</span></a></div>
</div>
<!--list title-->
<div class=LHotLableArticle id=LHotLable>
<ul>
    <li class=navta><img alt=热门标签 src="http://images.e800.com.cn/imginfo/default/rss3.gif" align=absMiddle></li>
    <li class=navbg><img src="http://images.e800.com.cn/imginfo/default/rss3a.gif" align=absMiddle>&nbsp;&nbsp;<a title=Java href="http://search.e800.com.cn/search?keyword=Java&amp;searchDomain=0"><u><font color=#0000ff>Java</font></u></a>&nbsp;&nbsp;<img src="http://images.e800.com.cn/imginfo/default/rss3b.gif" align=absMiddle></li>
    <li class=navbg><img src="http://images.e800.com.cn/imginfo/default/rss3a.gif" align=absMiddle>&nbsp;&nbsp;<a title=内存 href="http://search.e800.com.cn/search?keyword=%C4%DA%B4%E6&amp;searchDomain=0"><u><font color=#0000ff>内存</font></u></a>&nbsp;&nbsp;<img src="http://images.e800.com.cn/imginfo/default/rss3b.gif" align=absMiddle></li>
    <li class=navbg><img src="http://images.e800.com.cn/imginfo/default/rss3a.gif" align=absMiddle>&nbsp;&nbsp;<a title=泄漏 href="http://search.e800.com.cn/search?keyword=%D0%B9%C2%A9&amp;searchDomain=0"><u><font color=#0000ff>泄漏</font></u></a>&nbsp;&nbsp;<img src="http://images.e800.com.cn/imginfo/default/rss3b.gif" align=absMiddle></li>
    <li class=navbg><img src="http://images.e800.com.cn/imginfo/default/rss3a.gif" align=absMiddle>&nbsp;&nbsp;<a title=java href="http://search.e800.com.cn/search?keyword=java&amp;searchDomain=0"><u><font color=#0000ff>java</font></u></a>&nbsp;&nbsp;<img src="http://images.e800.com.cn/imginfo/default/rss3b.gif" align=absMiddle></li>
</ul>
</div>
<!--article Content -->
<div class=AreaCon>
<table>
    <tbody>
        <tr>
            <td>
            <div id=1><strong></strong><br><br>编者按：Java内存泄漏是每个Java程序员都会遇到的问题，程序在本地运行一切正常，可是布署到远端就会出现内存无限制的增长，最后系统瘫痪，那么如何最快最好的检测程序的稳定性，防止系统崩盘，作者用自已的亲身经历与各位网友分享解决这些问题的办法。 <br><br>作为Internet最流行的编程语言之一,Java现正非常流行。我们的网络应用程序就主要采用Java语言开发，大体上分为客户端、服务器和数据库三个层次。在进入测试过程中，我们发现有一个程序模块系统内存和CPU资源消耗急剧增加，持续增长到出现 java.lang.OutOfMemoryError为止。经过分析Java内存泄漏是破坏系统的主要因素。这里与大家分享我们在开发过程中遇到的 Java内存泄漏的检测和处理解决过程. <br><br><strong>一. Java是如何管理内存</strong> <br><br>为了判断Java中是否有内存泄露，我们首先必须了解Java是如何管理内存的。Java的内存管理就是对象的分配和释放问题。在Java中，内存的分配是由程序完成的，而内存的释放是由垃圾收集器(Garbage Collection，GC)完成的，程序员不需要通过调用函数来释放内存，但它只能回收无用并且不再被其它对象引用的那些对象所占用的空间。 <br><br>Java的内存垃圾回收机制是从程序的主要运行对象开始检查引用链，当遍历一遍后发现没有被引用的孤立对象就作为垃圾回收。GC为了能够正确释放对象，必须监控每一个对象的运行状态，包括对象的申请、引用、被引用、赋值等，GC都需要进行监控。监视对象状态是为了更加准确地、及时地释放对象，而释放对象的根本原则就是该对象不再被引用。 <br><br>在Java中，这些无用的对象都由GC负责回收，因此程序员不需要考虑这部分的内存泄露。虽然，我们有几个函数可以访问GC，例如运行GC的函数 System.gc()，但是根据Java语言规范定义，该函数不保证JVM的垃圾收集器一定会执行。因为不同的JVM实现者可能使用不同的算法管理 GC。通常GC的线程的优先级别较低。JVM调用GC的策略也有很多种，有的是内存使用到达一定程度时，GC才开始工作，也有定时执行的，有的是平缓执行 GC，有的是中断式执行GC。但通常来说，我们不需要关心这些。 <br></div>
            <div id=ParagraphCount style="DISPLAY: none">1</div>
            <div id=1>
            <p><strong></strong><br><br><strong>一. 什么是Java中的内存泄露</strong> <br><br>导致内存泄漏主要的原因是，先前申请了内存空间而忘记了释放。如果程序中存在对无用对象的引用，那么这些对象就会驻留内存，消耗内存，因为无法让垃圾回收器GC验证这些对象是否不再需要。如果存在对象的引用，这个对象就被定义为"有效的活动"，同时不会被释放。要确定对象所占内存将被回收，我们就要务必确认该对象不再会被使用。典型的做法就是把对象数据成员设为null或者从集合中移除该对象。但当局部变量不需要时，不需明显的设为null，因为一个方法执行完毕时，这些引用会自动被清理。 <br><br>在Java中，内存泄漏就是存在一些被分配的对象，这些对象有下面两个特点，首先，这些对象是有被引用的，即在有向树形图中，存在树枝通路可以与其相连；其次，这些对象是无用的，即程序以后不会再使用这些对象。如果对象满足这两个条件，这些对象就可以判定为Java中的内存泄漏，这些对象不会被GC所回收，然而它却占用内存。 <br><br>这里引用一个常看到的例子，在下面的代码中，循环申请Object对象，并将所申请的对象放入一个Vector中，如果仅仅释放对象本身，但因为 Vector仍然引用该对象，所以这个对象对GC来说是不可回收的。因此，如果对象加入到Vector后，还必须从Vector中删除，最简单的方法就是将Vector对象设置为null。 </p>
            <div style="OVERFLOW: auto; WIDTH: 500px">
            <pre style="BORDER-RIGHT: black 1px solid; PADDING-RIGHT: 4px; BORDER-TOP: black 1px solid; PADDING-LEFT: 4px; PADDING-BOTTOM: 4px; BORDER-LEFT: black 1px solid; PADDING-TOP: 4px; BORDER-BOTTOM: black 1px solid; BACKGROUND-COLOR: rgb(237,237,237)">
            <div><!--
            Code highlighting produced by Actipro CodeHighlighter (freeware)
            http://www.CodeHighlighter.com/
            --><img alt="" src="http://images.e800.com.cn/articles/2007/4/13/1176400876716None.gif" align=top><span style="COLOR: rgb(0,0,0)">Vector v </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> </span><span style="COLOR: rgb(0,0,255)">new</span><span style="COLOR: rgb(0,0,0)"> Vector(</span><span style="COLOR: rgb(0,0,0)">10</span><span style="COLOR: rgb(0,0,0)">);
            <img alt="" src="http://images.e800.com.cn/articles/2007/4/13/1176400876716None.gif" align=top></span><span style="COLOR: rgb(0,0,255)">for</span><span style="COLOR: rgb(0,0,0)"> (</span><span style="COLOR: rgb(0,0,255)">int</span><span style="COLOR: rgb(0,0,0)"> i </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> </span><span style="COLOR: rgb(0,0,0)">1</span><span style="COLOR: rgb(0,0,0)">; i </span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)"> </span><span style="COLOR: rgb(0,0,0)">100</span><span style="COLOR: rgb(0,0,0)">; i</span><span style="COLOR: rgb(0,0,0)">++</span><span style="COLOR: rgb(0,0,0)">)
            <img id=Codehighlighter_62_116_Open_Image onclick="this.style.display=''none''; Codehighlighter_62_116_Open_Text.style.display=''none''; Codehighlighter_62_116_Closed_Image.style.display=''inline''; Codehighlighter_62_116_Closed_Text.style.display=''inline'';" alt="" src="http://images.e800.com.cn/articles/2007/4/13/1176400876729ExpandedBlockStart.gif" align=top><img id=Codehighlighter_62_116_Closed_Image style="DISPLAY: none" onclick="this.style.display=''none''; Codehighlighter_62_116_Closed_Text.style.display=''none''; Codehighlighter_62_116_Open_Image.style.display=''inline''; Codehighlighter_62_116_Open_Text.style.display=''inline'';" alt="" src="http://images.e800.com.cn/articles/2007/4/13/1176400876735ContractedBlock.gif" align=top>...</span><span id=Codehighlighter_62_116_Closed_Text style="BORDER-RIGHT: rgb(128,128,128) 1px solid; BORDER-TOP: rgb(128,128,128) 1px solid; DISPLAY: none; BORDER-LEFT: rgb(128,128,128) 1px solid; BORDER-BOTTOM: rgb(128,128,128) 1px solid; BACKGROUND-COLOR: rgb(255,255,255)">...</span><span id=Codehighlighter_62_116_Open_Text><span style="COLOR: rgb(0,0,0)">{
            <img alt="" src="http://images.e800.com.cn/articles/2007/4/13/1176400876740InBlock.gif" align=top>　Object o </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> </span><span style="COLOR: rgb(0,0,255)">new</span><span style="COLOR: rgb(0,0,0)"> Object();
            <img alt="" src="http://images.e800.com.cn/articles/2007/4/13/1176400876740InBlock.gif" align=top>　v.add(o);
            <img alt="" src="http://images.e800.com.cn/articles/2007/4/13/1176400876740InBlock.gif" align=top>　o </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> </span><span style="COLOR: rgb(0,0,255)">null</span><span style="COLOR: rgb(0,0,0)">;
            <img alt="" src="http://images.e800.com.cn/articles/2007/4/13/1176400876770ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: rgb(0,128,0)">//</span><span style="COLOR: rgb(0,128,0)">此时，所有的Object对象都没有被释放，因为变量v引用这些对象。 </span></div>
            <br></pre>
            </div>
            <p>实际上这些对象已经是无用的，但还被引用，GC就无能为力了(事实上GC认为它还有用)，这一点是导致内存泄漏最重要的原因。再引用另一个例子来说明Java的内存泄漏。假设有一个日志类Logger，其提供一个静态的log(String msg)，任何其它类都可以调用Logger.Log(message)来将message的内容记录到系统的日志文件中。<br><br>Logger类有一个类型为HashMap的静态变量temp，每次在执行log(message)的时候，都首先将message的值写入temp中 (以当前线程+当前时间为键)，在退出之前再从temp中将以当前线程和当前时间为键的条目删除。注意，这里当前时间是不断变化的，所以log在退出之前执行删除条目的操作并不能删除执行之初写入的条目。这样，任何一个作为参数传给log的字符串最终由于被Logger的静态变量temp引用，而无法得到回收，这种对象保持就是我们所说的Java内存泄漏。 总的来说，内存管理中的内存泄漏产生的主要原因：保留下来却永远不再使用的对象引用。 </p>
            </div>
            <div id=ParagraphCount style="DISPLAY: none">1</div>
            <div id=1>
            <p><strong></strong><br><br><strong>三. 几种典型的内存泄漏</strong> <br><br>我们知道了在Java中确实会存在内存泄漏，那么就让我们看一看几种典型的泄漏，并找出他们发生的原因和解决方法。 <br><br><strong>3.1 全局集合</strong> <br>在大型应用程序中存在各种各样的全局数据仓库是很普遍的，比如一个JNDI-tree或者一个session table。在这些情况下，必须注意管理储存库的大小。必须有某种机制从储存库中移除不再需要的数据。 <br><br>通常有很多不同的解决形式，其中最常用的是一种周期运行的清除作业。这个作业会验证仓库中的数据然后清除一切不需要的数据。 <br><br>另一种管理储存库的方法是使用反向链接(referrer)计数。然后集合负责统计集合中每个入口的反向链接的数目。这要求反向链接告诉集合何时会退出入口。当反向链接数目为零时，该元素就可以从集合中移除了。 <br>　　 <br><strong>3.2 缓存</strong> <br>缓存一种用来快速查找已经执行过的操作结果的数据结构。因此，如果一个操作执行需要比较多的资源并会多次被使用，通常做法是把常用的输入数据的操作结果进行缓存，以便在下次调用该操作时使用缓存的数据。缓存通常都是以动态方式实现的,如果缓存设置不正确而大量使用缓存的话则会出现内存溢出的后果，因此需要将所使用的内存容量与检索数据的速度加以平衡。 <br><br>常用的解决途径是使用java.lang.ref.SoftReference类坚持将对象放入缓存。这个方法可以保证当虚拟机用完内存或者需要更多堆的时候，可以释放这些对象的引用。 <br><br><strong>3.3 类装载器</strong> <br>Java类装载器的使用为内存泄漏提供了许多可乘之机。一般来说类装载器都具有复杂结构，因为类装载器不仅仅是只与"常规"对象引用有关，同时也和对象内部的引用有关。比如数据变量，方法和各种类。这意味着只要存在对数据变量，方法，各种类和对象的类装载器，那么类装载器将驻留在JVM中。既然类装载器可以同很多的类关联，同时也可以和静态数据变量关联，那么相当多的内存就可能发生泄漏。 <br></p>
            </div>
            <div id=ParagraphCount style="DISPLAY: none">1</div>
            <div id=1>
            <p><strong></strong><br><strong><br>四. 如何检测和处理内存泄漏</strong> <br>如何查找引起内存泄漏的原因一般有两个步骤:第一是安排有经验的编程人员对代码进行走查和分析，找出内存泄漏发生的位置;第二是使用专门的内存泄漏测试工具进行测试。 <br><br>第一个步骤在代码走查的工作中，可以安排对系统业务和开发语言工具比较熟悉的开发人员对应用的代码进行了交叉走查，尽量找出代码中存在的数据库连接声明和结果集未关闭、代码冗余等故障代码。 <br><br>第二个步骤就是检测Java的内存泄漏。在这里我们通常使用一些工具来检查Java程序的内存泄漏问题。市场上已有几种专业检查Java内存泄漏的工具，它们的基本工作原理大同小异，都是通过监测Java程序运行时，所有对象的申请、释放等动作，将内存管理的所有信息进行统计、分析、可视化。开发人员将根据这些信息判断程序是否有内存泄漏问题。这些工具包括Optimizeit Profiler，JProbe Profiler，JinSight , Rational 公司的Purify等。 <br><br><strong>4.1检测内存泄漏的存在 </strong><br>这里我们将简单介绍我们在使用Optimizeit检查的过程。通常在知道发生内存泄漏之后，第一步是要弄清楚泄漏了什么数据和哪个类的对象引起了泄漏。 <br><br>一般说来，一个正常的系统在其运行稳定后其内存的占用量是基本稳定的，不应该是无限制的增长的。同样，对任何一个类的对象的使用个数也有一个相对稳定的上限，不应该是持续增长的。根据这样的基本假设，我们持续地观察系统运行时使用的内存的大小和各实例的个数，如果内存的大小持续地增长，则说明系统存在内存泄漏，如果特定类的实例对象个数随时间而增长（就是所谓的&#8220;增长率&#8221;），则说明这个类的实例可能存在泄漏情况。 <br><br>另一方面通常发生内存泄漏的第一个迹象是：在应用程序中出现了OutOfMemoryError。在这种情况下，需要使用一些开销较低的工具来监控和查找内存泄漏。虽然OutOfMemoryError也有可能应用程序确实正在使用这么多的内存；对于这种情况则可以增加JVM可用的堆的数量，或者对应用程序进行某种更改，使它使用较少的内存。 <br><br>但是，在许多情况下，OutOfMemoryError都是内存泄漏的信号。一种查明方法是不间断地监控GC的活动，确定内存使用量是否随着时间增加。如果确实如此，就可能发生了内存泄漏。</p>
            </div>
            <div id=ParagraphCount style="DISPLAY: none">1</div>
            <div id=1><strong><br><br>4.2处理内存泄漏的方法</strong> <br>一旦知道确实发生了内存泄漏，就需要更专业的工具来查明为什么会发生泄漏。JVM自己是不会告诉您的。这些专业工具从JVM获得内存系统信息的方法基本上有两种：JVMTI和字节码技术(byte code instrumentation)。Java虚拟机工具接口(Java Virtual Machine Tools Interface，JVMTI)及其前身Java虚拟机监视程序接口(Java Virtual Machine Profiling Interface，JVMPI)是外部工具与JVM通信并从JVM收集信息的标准化接口。字节码技术是指使用探测器处理字节码以获得工具所需的信息的技术。 <br><br>Optimizeit是Borland公司的产品，主要用于协助对软件系统进行代码优化和故障诊断，其中的Optimizeit Profiler主要用于内存泄漏的分析。Profiler的堆视图就是用来观察系统运行使用的内存大小和各个类的实例分配的个数的。 <br><br>首先，Profiler会进行趋势分析，找出是哪个类的对象在泄漏。系统运行长时间后可以得到四个内存快照。对这四个内存快照进行综合分析，如果每一次快照的内存使用都比上一次有增长，可以认定系统存在内存泄漏，找出在四个快照中实例个数都保持增长的类，这些类可以初步被认定为存在泄漏。通过数据收集和初步分析，可以得出初步结论:系统是否存在内存泄漏和哪些对象存在泄漏(被泄漏)。 <br><br>接下来，看看有哪些其他的类与泄漏的类的对象相关联。前面已经谈到Java中的内存泄漏就是无用的对象保持，简单地说就是因为编码的错误导致了一条本来不应该存在的引用链的存在(从而导致了被引用的对象无法释放)，因此内存泄漏分析的任务就是找出这条多余的引用链，并找到其形成的原因。查看对象分配到哪里是很有用的。同时只知道它们如何与其他对象相关联（即哪些对象引用了它们）是不够的，关于它们在何处创建的信息也很有用。 <br><br>最后，进一步研究单个对象，看看它们是如何互相关联的。借助于Profiler工具，应用程序中的代码可以在分配时进行动态添加，以创建堆栈跟踪。也有可以对系统中所有对象分配进行动态的堆栈跟踪。这些堆栈跟踪可以在工具中进行累积和分析。对每个被泄漏的实例对象，必然存在一条从某个牵引对象出发到达该对象的引用链。处于堆栈空间的牵引对象在被从栈中弹出后就失去其牵引的能力，变为非牵引对象。因此，在长时间的运行后，被泄露的对象基本上都是被作为类的静态变量的牵引对象牵引。 <br>总而言之, Java虽然有自动回收管理内存的功能,但内存泄漏也是不容忽视,它往往是破坏系统稳定性的重要因素。</div>
            </td>
        </tr>
    </tbody>
</table>
</div>
<img src ="http://www.blogjava.net/sutao/aggbug/133639.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sutao/" target="_blank">苏醄</a> 2007-07-31 18:35 <a href="http://www.blogjava.net/sutao/articles/133639.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java的内存泄漏</title><link>http://www.blogjava.net/sutao/articles/wolf00781.html</link><dc:creator>苏醄</dc:creator><author>苏醄</author><pubDate>Thu, 19 Jul 2007 01:38:00 GMT</pubDate><guid>http://www.blogjava.net/sutao/articles/wolf00781.html</guid><wfw:comment>http://www.blogjava.net/sutao/comments/131201.html</wfw:comment><comments>http://www.blogjava.net/sutao/articles/wolf00781.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sutao/comments/commentRss/131201.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sutao/services/trackbacks/131201.html</trackback:ping><description><![CDATA[<table id=content-table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr vAlign=top>
            <td style="COLOR: #0000ff" width="100%">
            <table cellSpacing=0 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr vAlign=top>
                        <td style="COLOR: #0000ff" width="100%">
                        <h1>Java的内存泄漏</h1>
                        <img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=1></td>
                        <td style="COLOR: #0000ff" width=192><img height=18 alt=developerWorks src="http://www-128.ibm.com/developerworks/cn/i/dw.gif" width=192></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr vAlign=top>
            <td style="COLOR: #0000ff" width=10><img height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></td>
            <td style="COLOR: #0000ff" width="100%">
            <table cellSpacing=0 cellPadding=0 width=160 align=right border=0>
                <tbody>
                    <tr>
                        <td style="COLOR: #0000ff" width=10><img height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></td>
                        <td style="COLOR: #0000ff">
                        <table cellSpacing=0 cellPadding=0 width=150 border=0>
                            <tbody>
                                <tr>
                                    <td style="COLOR: #0000ff">文档选项</td>
                                </tr>
                            </tbody>
                        </table>
                        <table cellSpacing=0 cellPadding=0 border=0>
                            <tbody>
                                <tr>
                                    <td style="COLOR: #0000ff" width=150><img height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8><input type=hidden value="Java的一个重要优点就是通过垃圾收集器(Garbage Collection，GC)自动管理内存的回收，程序员不需要通过调用函数来释放内存。因此，很多程序员认为Java不存在内存泄漏问题，或者认为即使有内存泄漏也不是程序的责任，而是GC或JVM的问题。其实，这种想法是不正确的，因为Java也存在内存泄露，但它的表现与C++不同。" name=body><input type=hidden value=Java的内存泄漏 name=subject><input type=hidden value=cn name=lang><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>
                                <table cellSpacing=0 cellPadding=0 width=143 border=0>
                                    <form name=email action=https://www-128.ibm.com/developerworks/secure/email-it.jsp>
                                    </form>
                                    <script language=JavaScript type=text/javascript>
                                    <!--
                                    document.write('
                                    <tr valign="top">
                                        <td width="8"><img src="//www.ibm.com/i/c.gif" src_cetemp="//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" src_cetemp="//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();" href_cetemp="javascript:document.email.submit();"><strong>将此页作为电子邮件发送</strong></a></p>
                                        </td>
                                    </tr>
                                    ');
                                    //-->
                                    </script>
                                    <tbody>
                                        <tr vAlign=top>
                                            <td style="COLOR: #0000ff" width=8><img height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></td>
                                            <td style="COLOR: #0000ff" width=16><img height=16 alt=将此页作为电子邮件发送 src="http://www.ibm.com/i/v14/icons/em.gif" width=16 vspace=3></td>
                                            <td style="COLOR: #0000ff" width=122>
                                            <p style="COLOR: #0000ff"><a href="javascript:document.email.submit();">将此页作为电子邮件发送</a></p>
                                            </td>
                                        </tr>
                                    </tbody>
                                </table>
                                </td>
                            </tr>
                        </tbody>
                    </table>
                    <!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- this content will be automatically generated across all content areas --><br><!--end RESERVED FOR FUTURE USE INCLUDE FILES--><br></td>
                </tr>
            </tbody>
        </table>
        <p style="COLOR: #0000ff">级别: 初级</p>
        <p style="COLOR: #0000ff"><a href="http://www-128.ibm.com/developerworks/cn/java/l-JavaMemoryLeak/#author">欧阳辰</a>, <br><a href="http://www-128.ibm.com/developerworks/cn/java/l-JavaMemoryLeak/#author">周欣</a>, <br></p>
        <p style="COLOR: #0000ff">2002 年 10 月 21 日</p>
        <blockquote>Java 的一个重要优点就是通过垃圾收集器(Garbage Collection，GC)自动管理内存的回收，程序员不需要通过调用函数来释放内存。因此，很多程序员认为Java不存在内存泄漏问题，或者认为即使有内存泄漏也不是程序的责任，而是GC或JVM的问题。其实，这种想法是不正确的，因为Java也存在内存泄露，但它的表现与C++不同。</blockquote><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--end RESERVED FOR FUTURE USE INCLUDE FILES-->
        <p style="COLOR: #0000ff"><a name=1>问题的提出</a></p>
        <p style="COLOR: #0000ff"></p>
        <p style="COLOR: #0000ff">Java 的一个重要优点就是通过垃圾收集器(Garbage Collection，GC)自动管理内存的回收，程序员不需要通过调用函数来释放内存。因此，很多程序员认为Java不存在内存泄漏问题，或者认为即使有内存泄漏也不是程序的责任，而是GC或JVM的问题。其实，这种想法是不正确的，因为Java也存在内存泄露，但它的表现与C++不同。</p>
        <p style="COLOR: #0000ff">随着越来越多的服务器程序采用Java技术，例如JSP，Servlet， EJB等，服务器程序往往长期运行。另外，在很多嵌入式系统中，内存的总量非常有限。内存泄露问题也就变得十分关键，即使每次运行少量泄漏，长期运行之后，系统也是面临崩溃的危险。</p>
        <br>
        <table cellSpacing=0 cellPadding=0 width="100%" border=0>
            <tbody>
                <tr>
                    <td style="COLOR: #0000ff"><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
                </tr>
            </tbody>
        </table>
        <table cellSpacing=0 cellPadding=0 align=right>
            <tbody>
                <tr align=right>
                    <td style="COLOR: #0000ff"><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
                    <table cellSpacing=0 cellPadding=0 border=0>
                        <tbody>
                            <tr>
                                <td style="COLOR: #0000ff" vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                                <td style="COLOR: #0000ff" vAlign=top align=right><a href="http://www-128.ibm.com/developerworks/cn/java/l-JavaMemoryLeak/#main">回页首</a></td>
                            </tr>
                        </tbody>
                    </table>
                    </td>
                </tr>
            </tbody>
        </table>
        <br><br>
        <p style="COLOR: #0000ff"><a name=2>Java是如何管理内存</a></p>
        <p style="COLOR: #0000ff"></p>
        <p style="COLOR: #0000ff">为了判断Java中是否有内存泄露，我们首先必须了解Java是如何管理内存的。Java的内存管理就是对象的分配和释放问题。在Java中，程序员需要通过关键字new为每个对象申请内存空间 (基本类型除外)，所有的对象都在堆 (Heap)中分配空间。另外，对象的释放是由GC决定和执行的。在Java中，内存的分配是由程序完成的，而内存的释放是有GC完成的，这种收支两条线的方法确实简化了程序员的工作。但同时，它也加重了JVM的工作。这也是Java程序运行速度较慢的原因之一。因为，GC为了能够正确释放对象，GC必须监控每一个对象的运行状态，包括对象的申请、引用、被引用、赋值等，GC都需要进行监控。</p>
        <p style="COLOR: #0000ff">监视对象状态是为了更加准确地、及时地释放对象，而释放对象的根本原则就是该对象不再被引用。</p>
        <p style="COLOR: #0000ff">为了更好理解GC的工作原理，我们可以将对象考虑为有向图的顶点，将引用关系考虑为图的有向边，有向边从引用者指向被引对象。另外，每个线程对象可以作为一个图的起始顶点，例如大多程序从main进程开始执行，那么该图就是以main进程顶点开始的一棵根树。在这个有向图中，根顶点可达的对象都是有效对象， GC将不回收这些对象。如果某个对象 (连通子图)与这个根顶点不可达(注意，该图为有向图)，那么我们认为这个(这些)对象不再被引用，可以被GC回收。</p>
        <p style="COLOR: #0000ff">以下，我们举一个例子说明如何用有向图表示内存管理。对于程序的每一个时刻，我们都有一个有向图表示JVM的内存分配情况。以下右图，就是左边程序运行到第6行的示意图。</p>
        <br><img height=201 alt=图1 src="http://www-128.ibm.com/developerworks/cn/java/l-JavaMemoryLeak/1.gif" width=582> <br>
        <p style="COLOR: #0000ff">Java 使用有向图的方式进行内存管理，可以消除引用循环的问题，例如有三个对象，相互引用，只要它们和根进程不可达的，那么GC也是可以回收它们的。这种方式的优点是管理内存的精度很高，但是效率较低。另外一种常用的内存管理技术是使用计数器，例如COM模型采用计数器方式管理构件，它与有向图相比，精度行低 (很难处理循环引用的问题)，但执行效率很高。</p>
        <br>
        <table cellSpacing=0 cellPadding=0 width="100%" border=0>
            <tbody>
                <tr>
                    <td style="COLOR: #0000ff"><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
                </tr>
            </tbody>
        </table>
        <table cellSpacing=0 cellPadding=0 align=right>
            <tbody>
                <tr align=right>
                    <td style="COLOR: #0000ff"><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
                    <table cellSpacing=0 cellPadding=0 border=0>
                        <tbody>
                            <tr>
                                <td style="COLOR: #0000ff" vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                                <td style="COLOR: #0000ff" vAlign=top align=right><a href="http://www-128.ibm.com/developerworks/cn/java/l-JavaMemoryLeak/#main">回页首</a></td>
                            </tr>
                        </tbody>
                    </table>
                    </td>
                </tr>
            </tbody>
        </table>
        <br><br>
        <p style="COLOR: #0000ff"><a name=3>什么是Java中的内存泄露</a></p>
        <p style="COLOR: #0000ff"></p>
        <p style="COLOR: #0000ff">下面，我们就可以描述什么是内存泄漏。在Java中，内存泄漏就是存在一些被分配的对象，这些对象有下面两个特点，首先，这些对象是可达的，即在有向图中，存在通路可以与其相连；其次，这些对象是无用的，即程序以后不会再使用这些对象。如果对象满足这两个条件，这些对象就可以判定为Java中的内存泄漏，这些对象不会被GC所回收，然而它却占用内存。</p>
        <p style="COLOR: #0000ff">在C++中，内存泄漏的范围更大一些。有些对象被分配了内存空间，然后却不可达，由于C++中没有GC，这些内存将永远收不回来。在Java中，这些不可达的对象都由GC负责回收，因此程序员不需要考虑这部分的内存泄露。</p>
        <p style="COLOR: #0000ff">通过分析，我们得知，对于C++，程序员需要自己管理边和顶点，而对于Java程序员只需要管理边就可以了(不需要管理顶点的释放)。通过这种方式，Java提高了编程的效率。</p>
        <br><img height=231 alt=图2 src="http://www-128.ibm.com/developerworks/cn/java/l-JavaMemoryLeak/2.gif" width=507> <br>
        <p style="COLOR: #0000ff">因此，通过以上分析，我们知道在Java中也有内存泄漏，但范围比C++要小一些。因为Java从语言上保证，任何对象都是可达的，所有的不可达对象都由GC管理。</p>
        <p style="COLOR: #0000ff">对于程序员来说，GC基本是透明的，不可见的。虽然，我们只有几个函数可以访问GC，例如运行GC的函数System.gc()，但是根据Java语言规范定义，该函数不保证JVM的垃圾收集器一定会执行。因为，不同的JVM实现者可能使用不同的算法管理GC。通常，GC的线程的优先级别较低。JVM调用GC的策略也有很多种，有的是内存使用到达一定程度时，GC才开始工作，也有定时执行的，有的是平缓执行GC，有的是中断式执行GC。但通常来说，我们不需要关心这些。除非在一些特定的场合，GC的执行影响应用程序的性能，例如对于基于Web的实时系统，如网络游戏等，用户不希望GC突然中断应用程序执行而进行垃圾回收，那么我们需要调整GC的参数，让GC能够通过平缓的方式释放内存，例如将垃圾回收分解为一系列的小步骤执行，Sun提供的HotSpot JVM就支持这一特性。</p>
        <p style="COLOR: #0000ff">下面给出了一个简单的内存泄露的例子。在这个例子中，我们循环申请Object对象，并将所申请的对象放入一个Vector中，如果我们仅仅释放引用本身，那么Vector仍然引用该对象，所以这个对象对GC来说是不可回收的。因此，如果对象加入到Vector后，还必须从Vector中删除，最简单的方法就是将Vector对象设置为null。</p>
        <table cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
            <tbody>
                <tr>
                    <td style="COLOR: #0000ff">
                    <pre>Vector v=new Vector(10);
                    for (int i=1;i&lt;100; i++)
                    {
                    Object o=new Object();
                    v.add(o);
                    o=null;
                    }</pre>
                    </td>
                </tr>
            </tbody>
        </table>
        <br>//此时，所有的Object对象都没有被释放，因为变量v引用这些对象。 <br>
        <table cellSpacing=0 cellPadding=0 width="100%" border=0>
            <tbody>
                <tr>
                    <td style="COLOR: #0000ff"><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
                </tr>
            </tbody>
        </table>
        <table cellSpacing=0 cellPadding=0 align=right>
            <tbody>
                <tr align=right>
                    <td style="COLOR: #0000ff"><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
                    <table cellSpacing=0 cellPadding=0 border=0>
                        <tbody>
                            <tr>
                                <td style="COLOR: #0000ff" vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                                <td style="COLOR: #0000ff" vAlign=top align=right><a href="http://www-128.ibm.com/developerworks/cn/java/l-JavaMemoryLeak/#main">回页首</a></td>
                            </tr>
                        </tbody>
                    </table>
                    </td>
                </tr>
            </tbody>
        </table>
        <br><br>
        <p style="COLOR: #0000ff"><a name=4>如何检测内存泄漏</a></p>
        <p style="COLOR: #0000ff"></p>
        <p style="COLOR: #0000ff">最后一个重要的问题，就是如何检测Java的内存泄漏。目前，我们通常使用一些工具来检查Java程序的内存泄漏问题。市场上已有几种专业检查Java内存泄漏的工具，它们的基本工作原理大同小异，都是通过监测Java程序运行时，所有对象的申请、释放等动作，将内存管理的所有信息进行统计、分析、可视化。开发人员将根据这些信息判断程序是否有内存泄漏问题。这些工具包括Optimizeit Profiler，JProbe Profiler，JinSight , Rational 公司的Purify等。</p>
        <p style="COLOR: #0000ff">下面，我们将简单介绍Optimizeit的基本功能和工作原理。</p>
        <p style="COLOR: #0000ff">Optimizeit Profiler版本4.11支持Application，Applet，Servlet和Romote Application四类应用，并且可以支持大多数类型的JVM，包括SUN JDK系列，IBM的JDK系列，和Jbuilder的JVM等。并且，该软件是由Java编写，因此它支持多种操作系统。Optimizeit系列还包括Thread Debugger和Code Coverage两个工具，分别用于监测运行时的线程状态和代码覆盖面。</p>
        <p style="COLOR: #0000ff">当设置好所有的参数了，我们就可以在OptimizeIt环境下运行被测程序，在程序运行过程中，Optimizeit可以监视内存的使用曲线(如下图)，包括JVM申请的堆(heap)的大小，和实际使用的内存大小。另外，在运行过程中，我们可以随时暂停程序的运行，甚至强行调用GC，让GC进行内存回收。通过内存使用曲线，我们可以整体了解程序使用内存的情况。这种监测对于长期运行的应用程序非常有必要，也很容易发现内存泄露。</p>
        <br><img height=354 alt=图3 src="http://www-128.ibm.com/developerworks/cn/java/l-JavaMemoryLeak/3.gif" width=521> <br>
        <p style="COLOR: #0000ff">在运行过程中，我们还可以从不同视角观查内存的使用情况，Optimizeit提供了四种方式：</p>
        <ul>
            <li>堆视角。 这是一个全面的视角，我们可以了解堆中的所有的对象信息(数量和种类)，并进行统计、排序，过滤。了解相关对象的变化情况。
            <li>方法视角。通过方法视角，我们可以得知每一种类的对象，都分配在哪些方法中，以及它们的数量。
            <li>对象视角。给定一个对象，通过对象视角，我们可以显示它的所有出引用和入引用对象，我们可以了解这个对象的所有引用关系。
            <li>引用图。 给定一个根，通过引用图，我们可以显示从该顶点出发的所有出引用。 </li>
        </ul>
        <p style="COLOR: #0000ff">在运行过程中，我们可以随时观察内存的使用情况，通过这种方式，我们可以很快找到那些长期不被释放，并且不再使用的对象。我们通过检查这些对象的生存周期，确认其是否为内存泄露。在实践当中，寻找内存泄露是一件非常麻烦的事情，它需要程序员对整个程序的代码比较清楚，并且需要丰富的调试经验，但是这个过程对于很多关键的Java程序都是十分重要的。</p>
        <p style="COLOR: #0000ff">综上所述，Java也存在内存泄露问题，其原因主要是一些对象虽然不再被使用，但它们仍然被引用。为了解决这些问题，我们可以通过软件工具来检查内存泄露，检查的主要原理就是暴露出所有堆中的对象，让程序员寻找那些无用但仍被引用的对象。</p>
        <br>
        <table cellSpacing=0 cellPadding=0 width="100%" border=0>
            <tbody>
                <tr>
                    <td style="COLOR: #0000ff"><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
                </tr>
            </tbody>
        </table>
        <table cellSpacing=0 cellPadding=0 align=right>
            <tbody>
                <tr align=right>
                    <td style="COLOR: #0000ff"><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
                    <table cellSpacing=0 cellPadding=0 border=0>
                        <tbody>
                            <tr>
                                <td style="COLOR: #0000ff" vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                                <td style="COLOR: #0000ff" vAlign=top align=right><a href="http://www-128.ibm.com/developerworks/cn/java/l-JavaMemoryLeak/#main">回页首</a></td>
                            </tr>
                        </tbody>
                    </table>
                    </td>
                </tr>
            </tbody>
        </table>
        <br><br>
        <p style="COLOR: #0000ff"><a name=resources>参考资料 </a></p>
        <p style="COLOR: #0000ff">文章:</p>
        <p style="COLOR: #0000ff">Jim Patrick, Handling memory leaks in Java programs,</p>
        <p style="COLOR: #0000ff">http://www.ibm.com/developerworks/library/j-leaks/index.html</p>
        <p style="COLOR: #0000ff">Ed Lycklama, Does Java Technology Have Memory Leaks?</p>
        <p style="COLOR: #0000ff"><a href="http://www.klgroup.com/javaone">http://www.klgroup.com/javaone</a> </p>
        <p style="COLOR: #0000ff">Sun,The Java HotSpot Virtual Machine, Technical White Paper</p>
        <p style="COLOR: #0000ff"></p>
        <p style="COLOR: #0000ff">软件：</p>
        <p style="COLOR: #0000ff">Sitraka Software's Jprobe <a href="http://www.sitraka.com/">http://www.sitraka.com</a> </p>
        <p style="COLOR: #0000ff">Boland Software's Optimizeit <a href="http://optimizeit/">http://optimizeit</a> </p>
        <p style="COLOR: #0000ff">IBM alphaWorks' Jinsight <a href="http://www.alphaworks.ibm.com/tech/jinsight">http://www.alphaworks.ibm.com/tech/jinsight</a> </p>
        <br>
        <table cellSpacing=0 cellPadding=0 width="100%" border=0>
            <tbody>
                <tr>
                    <td style="COLOR: #0000ff"><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
                </tr>
            </tbody>
        </table>
        <table cellSpacing=0 cellPadding=0 align=right>
            <tbody>
                <tr align=right>
                    <td style="COLOR: #0000ff"><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
                    <table cellSpacing=0 cellPadding=0 border=0>
                        <tbody>
                            <tr>
                                <td style="COLOR: #0000ff" vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                                <td style="COLOR: #0000ff" vAlign=top align=right><a href="http://www-128.ibm.com/developerworks/cn/java/l-JavaMemoryLeak/#main">回页首</a></td>
                            </tr>
                        </tbody>
                    </table>
                    </td>
                </tr>
            </tbody>
        </table>
        <br><br>
        <p style="COLOR: #0000ff"><a name=author>作者简介</a></p>
        <table cellSpacing=0 cellPadding=0 width="100%" border=0>
            <tbody>
                <tr>
                    <td style="COLOR: #0000ff" colSpan=3><img height=5 alt="" src="http://www.ibm.com/i/c.gif" width="100%"></td>
                </tr>
                <tr vAlign=top align=left>
                    <td style="COLOR: #0000ff">
                    <p style="COLOR: #0000ff"></p>
                    </td>
                    <td style="COLOR: #0000ff"><img height=5 alt="" src="http://www.ibm.com/i/c.gif" width=4></td>
                    <td style="COLOR: #0000ff" width="100%">
                    <p style="COLOR: #0000ff">欧阳辰，北京大学计算机硕士毕业，98年起开始研究基于java的软件开发、测试，参与开发、测试过多个基于Java的应用程序和Web服务项目。 </p>
                    </td>
                </tr>
            </tbody>
        </table>
        <br>
        <table cellSpacing=0 cellPadding=0 width="100%" border=0>
            <tbody>
                <tr>
                    <td style="COLOR: #0000ff" colSpan=3><img height=5 alt="" src="http://www.ibm.com/i/c.gif" width="100%"></td>
                </tr>
                <tr vAlign=top align=left>
                    <td style="COLOR: #0000ff">
                    <p style="COLOR: #0000ff"></p>
                    </td>
                    <td style="COLOR: #0000ff"><img height=5 alt="" src="http://www.ibm.com/i/c.gif" width=4></td>
                    <td style="COLOR: #0000ff" width="100%">
                    <p style="COLOR: #0000ff">周欣，北京大学计算机系在读博士生，主要研究方向：程序理解、逆向工程及软件度量，联系方式 <a href="http://www-128.ibm.com/developerworks/cn/java/l-JavaMemoryLeak/zhouxin@sei.pku.edu.cn">zhouxin@sei.pku.edu.cn</a> </p>
                    </td>
                </tr>
            </tbody>
        </table>
        </td>
    </tr>
</tbody>
</table>
<img src ="http://www.blogjava.net/sutao/aggbug/131201.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sutao/" target="_blank">苏醄</a> 2007-07-19 09:38 <a href="http://www.blogjava.net/sutao/articles/wolf00781.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JVM的垃圾回收机制详解和调优</title><link>http://www.blogjava.net/sutao/articles/131079.html</link><dc:creator>苏醄</dc:creator><author>苏醄</author><pubDate>Wed, 18 Jul 2007 07:03:00 GMT</pubDate><guid>http://www.blogjava.net/sutao/articles/131079.html</guid><wfw:comment>http://www.blogjava.net/sutao/comments/131079.html</wfw:comment><comments>http://www.blogjava.net/sutao/articles/131079.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sutao/comments/commentRss/131079.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sutao/services/trackbacks/131079.html</trackback:ping><description><![CDATA[<h4>JVM的垃圾回收机制详解和调优</h4>
<div class=tpc_content style="COLOR: #0000ff">版权声明：可以任意转载，转载时请务必以超链接形式标明文章原始出处和作者信息及本声明<br>原文地址:<br><a href="http://www.matrix.org.cn/resource/article/43/43769_JVM_GC_PDM.html" target=_blank><u><font color=#0000ff>http://www.matrix.org.cn/resource/article/43/43769_JVM_GC_PDM.html</font></u></a><br>关键字:JVM GC PDM<br><br><strong><br>1.JVM的gc概述</strong><br><br>&nbsp; gc即垃圾收集机制是指<u><font color=red>jvm</font></u>用于释放那些不再使用的对象所占用的内存。java语言并不要求<u><font color=red>jvm</font></u>有gc，也没有规定gc如何工作。不过常用的<u><font color=red>jvm</font></u>都有gc，而且大多数gc都使用类似的算法管理内存和执行收集操作。<br><br>&nbsp; 在充分理解了垃圾收集算法和执行过程后，才能有效的优化它的性能。有些垃圾收集专用于特殊的应用程序。比如，实时应用程序主要是为了避免垃圾收集中断，而大多数OLTP应用程序则注重整体效率。理解了应用程序的工作负荷和<u><font color=red>jvm</font></u>支持的垃圾收集算法，便可以进行优化配置垃圾收集器。<br><br>&nbsp; 垃圾收集的目的在于清除不再使用的对象。gc通过确定对象是否被活动对象引用来确定是否收集该对象。gc首先要判断该对象是否是时候可以收集。两种常用的方法是引用计数和对象引用遍历。<br><br><strong>1.1.引用计数</strong><br>引用计数存储对特定对象的所有引用数，也就是说，当应用程序创建引用以及引用超出范围时，<u><font color=red>jvm</font></u>必须适当增减引用数。当某对象的引用数为0时，便可以进行垃圾收集。<br><br><strong>1.2.对象引用遍历</strong><br>&nbsp; 早期的<u><font color=red>jvm</font></u>使用引用计数，现在大多数<u><font color=red>jvm</font></u>采用对象引用遍历。对象引用遍历从一组对象开始，沿着整个对象图上的每条链接，递归确定可到达（reachable）的对象。如果某对象不能从这些根对象的一个（至少一个）到达，则将它作为垃圾收集。在对象遍历阶段，gc必须记住哪些对象可以到达，以便删除不可到达的对象，这称为标记（marking）对象。<br><br>&nbsp; 下一步，gc要删除不可到达的对象。删除时，有些gc只是简单的扫描堆栈，删除未标记的未标记的对象，并释放它们的内存以生成新的对象，这叫做清除（sweeping）。这种方法的问题在于内存会分成好多小段，而它们不足以用于新的对象，但是组合起来却很大。因此，许多gc可以重新组织内存中的对象，并进行压缩（compact），形成可利用的空间。<br><br>&nbsp; 为此，gc需要停止其他的活动活动。这种方法意味着所有与应用程序相关的工作停止，只有gc运行。结果，在响应期间增减了许多混杂请求。另外，更复杂的 gc不断增加或同时运行以减少或者清除应用程序的中断。有的gc使用单线程完成这项工作，有的则采用多线程以增加效率。<br><br><strong>2.几种垃圾回收机制<br><br>2.1.标记－清除收集器</strong><br>这种收集器首先遍历对象图并标记可到达的对象，然后扫描堆栈以寻找未标记对象并释放它们的内存。这种收集器一般使用单线程工作并停止其他操作。<br><br><strong>2.2.标记－压缩收集器</strong><br>有时也叫标记－清除－压缩收集器，与标记－清除收集器有相同的标记阶段。在第二阶段，则把标记对象复制到堆栈的新域中以便压缩堆栈。这种收集器也停止其他操作。<br><br><strong>2.3.复制收集器</strong><br>这种收集器将堆栈分为两个域，常称为半空间。每次仅使用一半的空间，<u><font color=red>jvm</font></u>生成的新对象则放在另一半空间中。gc运行时，它把可到达对象复制到另一半空间，从而压缩了堆栈。这种方法适用于短生存期的对象，持续复制长生存期的对象则导致效率降低。<br><br><strong>2.4.增量收集器</strong><br>增量收集器把堆栈分为多个域，每次仅从一个域收集垃圾。这会造成较小的应用程序中断。<br><br><strong>2.5.分代收集器</strong><br>这种收集器把堆栈分为两个或多个域，用以存放不同寿命的对象。<u><font color=red>jvm</font></u>生成的新对象一般放在其中的某个域中。过一段时间，继续存在的对象将获得使用期并转入更长寿命的域中。分代收集器对不同的域使用不同的算法以优化性能。<br><br><strong>2.6.并发收集器</strong><br>并发收集器与应用程序同时运行。这些收集器在某点上（比如压缩时）一般都不得不停止其他操作以完成特定的任务，但是因为其他应用程序可进行其他的后台操作，所以中断其他处理的实际时间大大降低。<br><br><strong>2.7.并行收集器</strong><br>并行收集器使用某种传统的算法并使用多线程并行的执行它们的工作。在多cpu机器上使用多线程技术可以显著的提高java应用程序的可扩展性。<br><br><strong>3.Sun HotSpot 1.4.1 <u><font color=red>JVM</font></u>堆大小的调整</strong><br>&nbsp; <br>&nbsp; Sun HotSpot 1.4.1使用分代收集器，它把堆分为三个主要的域：新域、旧域以及永久域。<u><font color=red>Jvm</font></u>生成的所有新对象放在新域中。一旦对象经历了一定数量的垃圾收集循环后，便获得使用期并进入旧域。在永久域中<u><font color=red>jvm</font></u>则存储class和method对象。就配置而言，永久域是一个独立域并且不认为是堆的一部分。<br><br>&nbsp; 下面介绍如何控制这些域的大小。可使用-Xms和-Xmx 控制整个堆的原始大小或最大值。<br>&nbsp; 下面的命令是把初始大小设置为128M：<br>&nbsp; java &#8211;Xms128m <br>&nbsp; &#8211;Xmx256m为控制新域的大小，可使用-XX:NewRatio设置新域在堆中所占的比例。<br><br>&nbsp; 下面的命令把整个堆设置成128m，新域比率设置成3，即新域与旧域比例为1：3，新域为堆的1/4或32M：<br>&nbsp; java &#8211;Xms128m &#8211;Xmx128m <br>&nbsp; &#8211;XX:NewRatio =3可使用-XX:NewSize和-XX:MaxNewsize设置新域的初始值和最大值。<br><br>&nbsp; 下面的命令把新域的初始值和最大值设置成64m: <br>&nbsp; java &#8211;Xms256m &#8211;Xmx256m &#8211;Xmn64m<br>&nbsp; 永久域默认大小为4m。运行程序时，<u><font color=red>jvm</font></u>会调整永久域的大小以满足需要。每次调整时，<u><font color=red>jvm</font></u>会对堆进行一次完全的垃圾收集。<br><br>&nbsp; 使用-XX:MaxPerSize标志来增加永久域搭大小。在WebLogic Server应用程序加载较多类时，经常需要增加永久域的最大值。当<u><font color=red>jvm</font></u>加载类时，永久域中的对象急剧增加，从而使<u><font color=red>jvm</font></u>不断调整永久域大小。为了避免调整，可使用-XX:PerSize标志设置初始值。<br>&nbsp; 下面把永久域初始值设置成32m，最大值设置成64m。<br>&nbsp; java -Xms512m -Xmx512m -Xmn128m -XX:PermSize=32m -XX:MaxPermSize=64m<br><br>&nbsp; 默认状态下，HotSpot在新域中使用复制收集器。该域一般分为三个部分。第一部分为Eden，用于生成新的对象。另两部分称为救助空间，当Eden充满时，收集器停止应用程序，把所有可到达对象复制到当前的from救助空间，一旦当前的from救助空间充满，收集器则把可到达对象复制到当前的to救助空间。From和to救助空间互换角色。维持活动的对象将在救助空间不断复制，直到它们获得使用期并转入旧域。使用-XX:SurvivorRatio可控制新域子空间的大小。<br><br>&nbsp; 同NewRation一样，SurvivorRation规定某救助域与Eden空间的比值。比如，以下命令把新域设置成64m，Eden占32m，每个救助域各占16m：<br>&nbsp; java -Xms256m -Xmx256m -Xmn64m -XX:SurvivorRation =2<br><br>&nbsp; 如前所述，默认状态下HotSpot对新域使用复制收集器，对旧域使用标记－清除－压缩收集器。在新域中使用复制收集器有很多意义，因为应用程序生成的大部分对象是短寿命的。理想状态下，所有过渡对象在移出Eden空间时将被收集。如果能够这样的话，并且移出Eden空间的对象是长寿命的，那么理论上可以立即把它们移进旧域，避免在救助空间反复复制。但是，应用程序不能适合这种理想状态，因为它们有一小部分中长寿命的对象。最好是保持这些中长寿命的对象并放在新域中，因为复制小部分的对象总比压缩旧域廉价。为控制新域中对象的复制，可用-XX:TargetSurvivorRatio控制救助空间的比例（该值是设置救助空间的使用比例。如救助空间位1M，该值50表示可用500K）。该值是一个百分比，默认值是50。当较大的堆栈使用较低的 sruvivorratio时，应增加该值到80至90，以更好利用救助空间。用-XX:maxtenuring threshold可控制上限。<br><br>&nbsp; 为放置所有的复制全部发生以及希望对象从eden扩展到旧域，可以把MaxTenuring Threshold设置成0。设置完成后，实际上就不再使用救助空间了，因此应把SurvivorRatio设成最大值以最大化Eden空间，设置如下：<br>&nbsp; java &#8230; -XX:MaxTenuringThreshold=0 &#8211;XX:SurvivorRatio＝50000 &#8230;<br><br><strong>4.BEA JRockit <u><font color=red>JVM</font></u>的使用</strong><br>&nbsp; &nbsp; Bea WebLogic 8.1使用的新的<u><font color=red>JVM</font></u>用于Intel平台。在Bea安装完毕的目录下可以看到有一个类似于jrockit81sp1_141_03的文件夹。这就是Bea新<u><font color=red>JVM</font></u>所在目录。不同于HotSpot把Java字节码编译成本地码，它预先编译成类。JRockit还提供了更细致的功能用以观察<u><font color=red>JVM</font></u>的运行状态，主要是独立的GUI控制台（只能适用于使用Jrockit才能使用jrockit81sp1_141_03自带的console监控一些cpu及memory参数）或者WebLogic Server控制台。<br><br>Bea JRockit <u><font color=red>JVM</font></u>支持4种垃圾收集器：<br><strong>4.1.1.分代复制收集器 </strong><br>它与默认的分代收集器工作策略类似。对象在新域中分配，即JRockit文档中的nursery。这种收集器最适合单cpu机上小型堆操作。<br><br><strong>4.1.2.单空间并发收集器</strong><br>该收集器使用完整堆，并与背景线程共同工作。尽管这种收集器可以消除中断，但是收集器需花费较长的时间寻找死对象，而且处理应用程序时收集器经常运行。如果处理器不能应付应用程序产生的垃圾，它会中断应用程序并关闭收集。<br><br>分代并发收集器这种收集器在护理域使用排它复制收集器，在旧域中则使用并发收集器。由于它比单空间共同发生收集器中断频繁，因此它需要较少的内存，应用程序的运行效率也较高，注意，过小的护理域可以导致大量的临时对象被扩展到旧域中。这会造成收集器超负荷运作，甚至采用排它性工作方式完成收集。<br><br><strong>4.1.3.并行收集器</strong><br>该收集器也停止其他进程的工作，但使用多线程以加速收集进程。尽管它比其他的收集器易于引起长时间的中断，但一般能更好的利用内存，程序效率也较高。<br><br>默认状态下，JRockit使用分代并发收集器。要改变收集器，可使用-Xgc:&lt;gc_name&gt;，对应四个收集器分别为gencopy， singlecon，gencon以及parallel。可使用-Xms和-Xmx设置堆的初始大小和最大值。要设置护理域，则使用-Xns:java &#8211;jrockit &#8211;Xms512m &#8211;Xmx512m &#8211;Xgc:gencon &#8211;Xns128m&#8230;尽管JRockit支持-verbose:gc开关，但它输出的信息会因收集器的不同而异。JRockit还支持memory、 load和codegen的输出。<br><br>注意 ：如果 使用JRockit <u><font color=red>JVM</font></u>的话还可以使用WLS自带的console（C:\bea\jrockit81sp1_141_03\bin下）来监控一些数据，如cpu，memery等。要想能构监控必须在启动服务时startWeblogic.cmd中加入－Xmanagement参数。<br><br><strong>5.如何从<u><font color=red>JVM</font></u>中获取信息来进行调整</strong><br><br>-verbose.gc 开关可显示gc的操作内容。打开它，可以显示最忙和最空闲收集行为发生的时间、收集前后的内存大小、收集需要的时间等。打开-xx:+ printgcdetails开关，可以详细了解gc中的变化。打开-XX: + PrintGCTimeStamps开关，可以了解这些垃圾收集发生的时间，自<u><font color=red>jvm</font></u>启动以后以秒计量。最后，通过-xx: + PrintHeapAtGC开关了解堆的更详细的信息。为了了解新域的情况，可以通过-XX:=PrintTenuringDistribution开关了解获得使用期的对象权。<br><br><strong>6.Pdm系统<u><font color=red>JVM</font></u>调整<br><br>6.1.服务器：前提内存1G 单CPU</strong><br><br>可通过如下参数进行调整：－server 启用服务器模式（如果CPU多，服务器机建议使用此项）<br><br>－Xms,－Xmx一般设为同样大小。 &nbsp; &nbsp; &nbsp; &nbsp; 800m<br>－Xmn 是将NewSize与MaxNewSize设为一致。320m<br>－XX:PerSize &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 64m<br>－XX:NewSize &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 320m 此值设大可调大新对象区，减少Full GC次数<br>－XX:MaxNewSize &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 320m<br>－XX:NewRato &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; NewSize设了可不设。4 <br>－XX: SurvivorRatio &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 4<br>－XX:userParNewGC &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 可用来设置并行收集 &nbsp; <br>－XX:ParallelGCThreads &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 可用来增加并行度 &nbsp; &nbsp; 4<br>－XXUseParallelGC &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 设置后可以使用并行清除收集器<br>－XX：UseAdaptiveSizePolicy &nbsp; &nbsp; &nbsp; 与上面一个联合使用效果更好，利用它可以自动优化新域大小以及救助空间比值<br><br><strong>6.2.客户机：通过在JNLP文件中设置参数来调整客户端<u><font color=red>JVM</font></u></strong><br><br>JNLP中参数：initial-heap-size和max-heap-size<br><br>这可以在framework的RequestManager中生成JNLP文件时加入上述参数，但是这些值是要求根据客户机的硬件状态变化的（如客户机的内存大小等）。建议这两个参数值设为客户机可用内存的60％（有待测试）。为了在动态生成JNLP时以上两个参数值能够随客户机不同而不同，可靠虑获得客户机系统信息并将这些嵌到首页index.jsp中作为连接请求的参数。<br><br>在设置了上述参数后可以通过Visualgc 来观察垃圾回收的一些参数状态，再做相应的调整来改善性能。一般的标准是减少fullgc的次数，最好硬件支持使用并行垃圾回收（要求多CPU）。</div>
<img src ="http://www.blogjava.net/sutao/aggbug/131079.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sutao/" target="_blank">苏醄</a> 2007-07-18 15:03 <a href="http://www.blogjava.net/sutao/articles/131079.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>