﻿<?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-JavaGis-文章分类-Java</title><link>http://www.blogjava.net/zdygis/category/14775.html</link><description>JavaGis大草原</description><language>zh-cn</language><lastBuildDate>Fri, 02 Mar 2007 07:42:33 GMT</lastBuildDate><pubDate>Fri, 02 Mar 2007 07:42:33 GMT</pubDate><ttl>60</ttl><item><title>深入剖析Java编程中的中文问题及建议最优解决方法--上篇</title><link>http://www.blogjava.net/zdygis/articles/67570.html</link><dc:creator>zdygis</dc:creator><author>zdygis</author><pubDate>Mon, 04 Sep 2006 06:03:00 GMT</pubDate><guid>http://www.blogjava.net/zdygis/articles/67570.html</guid><wfw:comment>http://www.blogjava.net/zdygis/comments/67570.html</wfw:comment><comments>http://www.blogjava.net/zdygis/articles/67570.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zdygis/comments/commentRss/67570.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zdygis/services/trackbacks/67570.html</trackback:ping><description><![CDATA[
		<strong>
				<p>
						<b>Abstract：</b>本文深入分析了Java程序设计中Java编译器对java源文件和JVM对class类文件的编码/解码过程，通过此过程的解析透视出了Java编程中中文问题产生的根本原因，最后给出了建议的最优化的解决Java中文问题的方法。</p>
				<p>
						<b>1、中文问题的来源 </b>
						<br />    计算机最初的操作系统支持的编码是单字节的字符编码，于是，在计算机中一切处理程序最初都是以单字节编码的英文为准进行处理。随着计算机的发展，为了适应世界其它民族的语言（当然包括我们的汉字），人们提出了UNICODE编码，它采用双字节编码，兼容英文字符和其它民族的双字节字符编码，所以，目前，大多数国际性的软件内部均采用UNICODE编码，在软件运行时，它获得本地支持系统（多数时间是操作系统）默认支持的编码格式，然后再将软件内部的UNICODE转化为本地系统默认支持的格式显示出来。Java的JDK和JVM即是如此，我这里说的JDK是指国际版的JDK，我们大多数程序员使用的是国际化的JDK版本，以下所有的JDK均指国际化的JDK版本。我们的汉字是双字节编码语言，为了能让计算机处理中文，我们自己制定的gb2312、GBK、GBK2K等标准以适应计算机处理的需求。所以，大部分的操作系统为了适应我们处理中文的需求，均定制有中文操作系统，它们采用的是GBK,GB2312编码格式以正确显示我们的汉字。如：中文Win2K默认采用的是GBK编码显示，在中文WIN2k中保存文件时默认采用的保存文件的编码格式也是GBK的，即，所有在中文WIN2K中保存的文件它的内部编码默认均采用GBK编码，注意：GBK是在GB2312基础上扩充来的。<br />    由于Java语言内部采用UNICODE编码，所以在JAVA程序运行时，就存在着一个从UNICODE编码和对应的操作系统及浏览器支持的编码格式转换输入、输出的问题，这个转换过程有着一系列的步骤，如果其中任何一步出错，则显示出来的汉字就会出是乱码，这就是我们常见的JAVA中文问题。<br />    同时，Java是一个跨平台的编程语言，也即我们编写的程序不仅能在中文windows上运行，也能在中文Linux等系统上运行，同时也要求能在英文等系统上运行（我们经常看到有人把在中文win2k上编写的JAVA程序，移植到英文Linux上运行）。这种移植操作也会带来中文问题。<br />    还有，有人使用英文的操作系统和英文的IE等浏览器，来运行带中文字符的程序和浏览中文网页，它们本身就不支持中文，也会带来中文问题。<br />    有，几乎所有的浏览器默认在传递参数时都是以UTF-8编码格式来传递，而不是按中文编码传递，所以，传递中文参数时也会有问题，从而带来乱码现象。<br />    总之，以上几个方面是JAVA中的中文问题的主要来源，我们把以上原因造成的程序不能正确运行而产生的问题称作：JAVA中文问题。<br /><b>2、JAVA编码转换的详细过程 </b><br />    我们常见的JAVA程序包括以下类别：<br />     *直接在console上运行的类(包括可视化界面的类)<br />     *JSP代码类（注：JSP是Servlets类的变型）<br />     *Servelets类<br />     *EJB类<br />     *其它不可以直接运行的支持类<br />    这些类文件中，都有可能含有中文字符串，并且我们常用前三类JAVA程序和用户直接交互，用于输出和输入字符，如：我们在JSP和Servlet中得到客户端送来的字符，这些字符也包括中文字符。无论这些JAVA类的作用如何，这些JAVA程序的生命周期都是这样的：<br />    *编程人员在一定的操作系统上选择一个合适的编辑软件来实现源程序代码并以.java扩展名保存在操作系统中，例如我们在中文win2k中用记事本编辑一个java源程序；<br />     *编程人员用JDK中的javac.exe来编译这些源代码，形成.class类(JSP文件是由容器调用JDK来编译的)；<br />     *直接运行这些类或将这些类布署到WEB容器中去运行，并输出结果。<br />    那么，在这些过程中，JDK和JVM是如何将这些文件如何编码和解码并运行的呢？<br />    这里，我们以中文win2k操作系统为例说明JAVA类是如何来编码和被解码的。<br />    第一步，我们在中文win2k中用编辑软件如记事本编写一个Java源程序文件(包括以上五类JAVA程序)，程序文件在保存时默认采用了操作系统默认支持GBK编码格式(操作系统默认支持的格式为file.encoding格式)形成了一个.java文件，也即，java程序在被编译前，我们的JAVA源程序文件是采用操作系统默认支持的file.encoding编码格式保存的，java源程序中含有中文信息字符和英文程序代码；要查看系统的file.encoding参数，可以用以下代码：<br />public class ShowSystemDefaultEncoding {<br />public static void main(String[] args) {<br />String encoding = System.getProperty("file.encoding");<br />System.out.println(encoding);<br />}}<br />    第二步，我们用JDK的javac.exe文件编译我们的Java源程序，由于JDK是国际版的，在编译的时候，如果我们没有用-encoding参数指定我们的JAVA源程序的编码格式，则javac.exe首先获得我们操作系统默认采用的编码格式，也即在编译java程序时，若我们不指定源程序文件的编码格式，JDK首先获得操作系统的file.encoding参数(它保存的就是操作系统默认的编码格式，如WIN2k，它的值为GBK)，然后JDK就把我们的java源程序从file.encoding编码格式转化为JAVA内部默认的UNICODE格式放入内存中。然后，javac把转换后的unicode格式的文件进行编译成.class类文件，此时.class文件是UNICODE编码的，它暂放在内存中，紧接着，JDK将此以UNICODE编码的编译后的class文件保存到我们的操作系统中形成我们见到的.class文件。对我们来说，我们最终获得的.class文件是内容以UNICODE编码格式保存的类文件，它内部包含我们源程序中的中文字符串，只不过此时它己经由file.encoding格式转化为UNICODE格式了。<br />    这一步中，对于JSP源程序文件是不同的，对于JSP，这个过程是这样的：即WEB容器调用JSP编译器，JSP编译器先查看JSP文件中是否设置有文件编码格式，如果JSP文件中没有设置JSP文件的编码格式，则JSP编译器调用JDK先把JSP文件用JVM默认的字符编码格式(也即WEB容器所在的操作系统的默认的file.encoding)转化为临时的Servlet类，然后再把它编译成UNICODE格式的class类，并保存在临时文件夹中。如：在中文win2k上，WEB容器就把JSP文件从GBK编码格式转化为UNICODE格式，然后编译成临时保存的Servlet类，以响应用户的请求。<br />    第三步，运行第二步编译出来的类，分为三种情况：<br />    A、 直接在console上运行的类<br />    B、 EJB类和不可以直接运行的支持类(如JavaBean类)<br />    C、 JSP代码和Servlet类<br />    D、 JAVA程序和数据库之间<br />    下面我们分这四种情况来看。<br />    A、直接在console上运行的类<br />    这种情况，运行该类首先需要JVM支持，即操作系统中必须安装有JRE。运行过程是这样的：首先java启动JVM，此时JVM读出操作系统中保存的class文件并把内容读入内存中，此时内存中为UNICODE格式的class类，然后JVM运行它，如果此时此类需要接收用户输入，则类会默认用file.encoding编码格式对用户输入的串进行编码并转化为unicode保存入内存（用户可以设置输入流的编码格式）。程序运行后，产生的字符串（UNICODE编码的）再回交给JVM，最后JRE把此字符串再转化为file.encoding格式(用户可以设置输出流的编码格式)传递给操作系统显示接口并输出到界面上。<br />    对于这种直接在console上运行的类，它的转化过程可用图1更加明确的表示出来：</p>
				<p>
						<img style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; BORDER-LEFT: black 1px solid; BORDER-BOTTOM: black 1px solid" src="http://java.ccidnet.com/col/attachment/2006/6/713009.gif" />
				</p>
				<p>图1<br />    以上每一步的转化都需要正确的编码格式转化，才能最终不出现乱码现象。<br />    B、EJB类和不可以直接运行的支持类(如JavaBean类)<br />    由于EJB类和不可以直接运行的支持类，它们一般不与用户直接交互输入和输出，它们常常与其它的类进行交互输入和输出，所以它们在第二步被编译后，就形成了内容是UNICODE编码的类保存在操作系统中了，以后只要它与其它的类之间的交互在参数传递过程中没有丢失，则它就会正确的运行。<br />这种EJB类和不可以直接运行的支持类, 它的转化过程可用图2更加明确的表示出来：</p>
				<p>    </p>
				<p>
						<img style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; BORDER-LEFT: black 1px solid; BORDER-BOTTOM: black 1px solid" src="http://java.ccidnet.com/col/attachment/2006/6/713011.gif" />
				</p>
				<p>图2<br />C、JSP代码和Servlet类<br />    经过第二步后，JSP文件也被转化为Servlets类文件，只不过它不像标准的Servlets一校存在于classes目录中，它存在于WEB容器的临时目录中，故这一步中我们也把它做为Servlets来看。<br />    对于Servlets，客户端请求它时，WEB容器调用它的JVM来运行Servlet，首先，JVM把Servlet的class类从系统中读出并装入内存中，内存中是以UNICODE编码的Servlet类的代码，然后JVM在内存中运行该Servlet类，如果Servlet在运行的过程中，需要接受从客户端传来的字符如：表单输入的值和URL中传入的值，此时如果程序中没有设定接受参数时采用的编码格式，则WEB容器会默认采用ISO-8859-1编码格式来接受传入的值并在JVM中转化为UNICODE格式的保存在WEB容器的内存中。Servlet运行后生成输出，输出的字符串是UNICODE格式的，紧接着，容器将Servlet运行产生的UNICODE格式的串（如html语法，用户输出的串等）直接发送到客户端浏览器上并输出给用户，如果此时指定了发送时输出的编码格式，则按指定的编码格式输出到浏览器上，如果没有指定，则默认按ISO-8859-1编码发送到客户的浏览器上。这种JSP代码和Servlet类，它的转化过程可用图3更加明确地表示出来：</p>
				<p>
						<img style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; BORDER-LEFT: black 1px solid; BORDER-BOTTOM: black 1px solid" src="http://java.ccidnet.com/col/attachment/2006/6/713013.gif" />
				</p>
				<p>图3<br />    D、Java程序和数据库之间<br />    对于几乎所有数据库的JDBC驱动程序，默认的在JAVA程序和数据库之间传递数据都是以ISO-8859-1为默认编码格式的，所以，我们的程序在向数据库内存储包含中文的数据时，JDBC首先是把程序内部的UNICODE编码格式的数据转化为ISO-8859-1的格式，然后传递到数据库中，在数据库保存数据时，它默认即以ISO-8859-1保存，所以，这是为什么我们常常在数据库中读出的中文数据是乱码。<br />    对于JAVA程序和数据库之间的数据传递，我们可以用图4清晰地表示出来</p>
				<p>
						<img style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; BORDER-LEFT: black 1px solid; BORDER-BOTTOM: black 1px solid" src="http://java.ccidnet.com/col/attachment/2006/6/713015.gif" />
				</p>
				<p>图4<br />    <b>3、分析常见的JAVA中文问题几个必须清楚的原则</b><br />    首先，经过上面的详细分析，我们可以清晰地看到，任何JAVA程序的生命期中，其编码转换的关键过程是在于：最初编译成class文件的转码和最终向用户输出的转码过程。<br />    其次，我们必须了解JAVA在编译时支持的、常用的编码格式有以下几种：<br />    *ISO-8859-1，8-bit, 同8859_1,ISO-8859-1,ISO_8859_1等编码<br />    *Cp1252，美国英语编码，同ANSI标准编码<br />    *UTF-8，同unicode编码<br />    *GB2312，同gb2312-80,gb2312-1980等编码<br />    *GBK , 同MS936，它是gb2312的扩充<br />    及其它的编码，如韩文、日文、繁体中文等。同时，我们要注意这些编码间的兼容关体系如下：<br />    unicode和UTF-8编码是一一对应的关系。GB2312可以认为是GBK的子集，即GBK编码是在gb2312上扩展来的。同时，GBK编码包含了20902个汉字，编码范围为：0x8140-0xfefe，所有的字符可以一一对应到UNICODE2.0中来。<br />    再次，对于放在操作系统中的.java源程序文件，在编译时，我们可以指定它内容的编码格式，具体来说用-encoding来指定。注意：如果源程序中含有中文字符，而你用-encoding指定为其它的编码字符，显然是要出错的。用-encoding指定源文件的编码方式为GBK或gb2312，无论我们在什么系统上编译含有中文字符的JAVA源程序都不会有问题，它都会正确地将中文转化为UNICODE存储在class文件中。<br />    <br />然后，我们必须清楚，几乎所有的WEB容器在其内部默认的字符编码格式都是以ISO-8859-1为默认值的，同时，几乎所有的浏览器在传递参数时都是默认以UTF-8的方式来传递参数的。所以，虽然我们的Java源文件在出入口的地方指定了正确的编码方式，但其在容器内部运行时还是以ISO-8859-1来处理的。<br /></p>
		</strong>
<img src ="http://www.blogjava.net/zdygis/aggbug/67570.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zdygis/" target="_blank">zdygis</a> 2006-09-04 14:03 <a href="http://www.blogjava.net/zdygis/articles/67570.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>提升J2EE的性能</title><link>http://www.blogjava.net/zdygis/articles/67272.html</link><dc:creator>zdygis</dc:creator><author>zdygis</author><pubDate>Sat, 02 Sep 2006 06:36:00 GMT</pubDate><guid>http://www.blogjava.net/zdygis/articles/67272.html</guid><wfw:comment>http://www.blogjava.net/zdygis/comments/67272.html</wfw:comment><comments>http://www.blogjava.net/zdygis/articles/67272.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zdygis/comments/commentRss/67272.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zdygis/services/trackbacks/67272.html</trackback:ping><description><![CDATA[
		<div>你的J2EE应用是不是运行的很慢？它们能不能承受住不断上升的访问量？本文讲述了开发高性能、高弹性的JSP页面和Servlet的性能优化技术。其意思是建立尽可能快的并能适应数量增长的用户及其请求。在本文中，我将带领你学习已经实践和得到证实的性能调整技术，它将大大地提高你的servlet和jsp页面的性能，进而提升J2EE的性能。这些技术的部分用于开发阶段，例如，设计和编码阶段。另一部分技术则与配置相关。 
<p>　　技术1：在HttpServletinit()方法中缓存数据</p><p>　　<a href="http://www.baidu.com/baidu?tn=sayyes&amp;word=服务器" target="_blank"><span class="unnamed8"><font color="#0000ff"><u>服务器</u></font></span></a>会在创建servlet实例之后和servlet处理任何请求之前调用servlet的init()方法。该方法在servlet的生命周期中仅调用一次。为了提高性能，在init()中缓存静态数据或完成要在初始化期间完成的代价昂贵的操作。例如，一个最佳实践是使用实现了javax.sql.DataSource接口的JDBC连接池。</p><p>　　DataSource从JNDI树中获得。每调用一次SQL就要使用JNDI查找DataSource是非常昂贵的工作，而且严重影响了应用的性能。Servlet的init()方法可以用于获取DataSource并缓存它以便之后的重用：</p><p>publicclassControllerServletextendsHttpServlet<br />{<br />privatejavax.sql.DataSourcetestDS=null;</p><p>publicvoidinit(ServletConfigconfig)throwsServletException<br />{<br />super.init(config);<br />Contextctx=null;<br />try<br />{<br />ctx=newInitialContext();<br />testDS=(javax.sql.DataSource)ctx.lookup("jdbc/testDS");<br />}<br />catch(NamingExceptionne)<br />{<br />ne.printStackTrace();<br />}<br />catch(Exceptione)<br />{<br />e.printStackTrace();<br />}<br />}</p><p>publicjavax.sql.DataSourcegetTestDS()<br />{<br />returntestDS;<br />}<br />...<br />...<br />} </p><p>　　技术2：禁用servlet和Jsp的自动装载功能</p><p>　　当每次修改了Servlet/JSP之后，你将不得不重新启动<a href="http://www.baidu.com/baidu?tn=sayyes&amp;word=服务器" target="_blank"><span class="unnamed8"><font color="#0000ff"><u>服务器</u></font></span></a>。由于自动装载功能减少开发时间，该功能被认为在开发阶段是非常有用的。但是，它在运行阶段是非常昂贵的；servlet/JSP由于不必要的装载，增加类装载器的负担而造成很差的性能。同样，这会使你的应用由于已被某种类装载器装载的类不能和当前类装载器装载的类不能相互协作而出现奇怪的冲突现象。因此，在运行环境中为了得到更好的性能，关闭servlet/JSP的自动装载功能。</p><p>　　技术3：控制HttpSession</p><p>　　许多应用需要一系列客户端的请求，因此他们能互相相关联。由于HTTP协议是无状态的，所以基于Web的应用需要负责维护这样一个叫做session的状态。为了支持必须维护状态的应用，Javaservlet技术提供了管理session和允许多种机制实现session的API。HttpSession对象扮演了session，但是使用它需要成本。无论何时HttpSession被使用和重写，它都由servlet读取。你可以通过使用下面的技术来提高性能：<br />l在JSP页面中不要创建默认的HttpSession:默认情况下，JSP页面创建HttpSession。如果你在JSP页面中不用HttpSession，为了节省性能开销，使用下边的页面指令可以避免自动创建HttpSession对象：<br />＜<a href="mailto:%@pagesession=&quot;false&quot;%"><font color="#000000">%@pagesession="false"%</font></a>＞</p><p>　　1) 不要将大的对象图存储在HttpSession中：如果你将数据当作一个大的对象图存储在HttpSession中，应用<a href="http://www.baidu.com/baidu?tn=sayyes&amp;word=服务器" target="_blank"><span class="unnamed8"><font color="#0000ff"><u>服务器</u></font></span></a>每次将不得不处理整个HttpSession对象。这将迫使Java序列化和增加计算开销。由于序列化的开销，随着存储在HttpSession对象中数据对象的增大，系统的吞吐量将会下降。</p><p>　　2) 用完后释放HttpSession：当不在使用HttpSession时，使用HttpSession.invalidate()方法使sesion失效。</p><p>　　3) 设置超时值：一个servlet引擎有一个默认的超时值。如果你不删除session或者一直把session用到它超时的时候，servlet引擎将把session从内存中删除。由于在内存和垃圾收集上的开销，session的超时值越大，它对系统弹性和性能的影响也越大。试着将session的超时值设置的尽可能低。</p><p>　　技术4：使用gzip压缩</p><p>　　压缩是删除冗余信息的作法，用尽可能小的空间描述你的信息。使用gzip（GNUzip）压缩文档能有效地减少下载HTML文件的时间。你的信息量越小，它们被送出的速度越快。因此，如果你压缩了由你web应用产生的内容，它到达用户并显示在用户屏幕上的速度就越快。不是任何<a href="http://www.baidu.com/baidu?tn=sayyes&amp;word=浏览器" target="_blank"><span class="unnamed8"><font color="#0000ff"><u>浏览器</u></font></span></a>都支持gzip压缩的，但检查一个<a href="http://www.baidu.com/baidu?tn=sayyes&amp;word=浏览器" target="_blank"><span class="unnamed8"><font color="#0000ff"><u>浏览器</u></font></span></a>是否支持它并发送gzip压缩内容到<a href="http://www.baidu.com/baidu?tn=sayyes&amp;word=浏览器" target="_blank"><span class="unnamed8"><font color="#0000ff"><u>浏览器</u></font></span></a>是很容易的事情。下边的代码段说明了如何发送压缩的内容。</p><p>publicvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)<br />throwsIOException,ServletException<br />{</p><p>OutputStreamout=null</p><p>//ChecktheAccepting-EncodingheaderfromtheHTTPrequest.<br />//Iftheheaderincludesgzip,chooseGZIP.<br />//Iftheheaderincludescompress,chooseZIP.<br />//Otherwisechoosenocompression.</p><p>Stringencoding=request.getHeader("Accept-Encoding");</p><p>if(encoding!=null&amp;&amp;encoding.indexOf("gzip")!=-1)<br />{<br />response.setHeader("Content-Encoding","gzip");<br />out=newGZIPOutputStream(response.getOutputStream());<br />}<br />elseif(encoding!=null&amp;&amp;encoding.indexOf("compress")!=-1)<br />{<br />response.setHeader("Content-Encoding","compress");<br />out=newZIPOutputStream(response.getOutputStream());<br />}<br />else<br />{<br />out=response.getOutputStream();</p><p>}<br />...<br />...<br />} </p><p>　　技术5：不要使用SingleThreadModel</p><p>　　SingleThreadModel保证servlet一次仅处理一个请求。如果一个servlet实现了这个接口，servlet引擎将为每个新的请求创建一个单独的servlet实例，这将引起大量的系统开销。如果你需要解决线程安全问题，请使用其他的办法替代这个接口。SingleThreadModel在Servlet2.4中是不再提倡使用。</p><p>　　技术6：使用线程池</p><p>　　servlet引擎为每个请求创建一个单独的线程，将该线程指派给service()方法，然后在service()方法执行完后删除该线程。默认情况下，servlet引擎可能为每个请求创建一个新的线程。由于创建和删除线程的开销是很昂贵的，于是这种默认行为降低了系统的性能。我们可以使用线程池来提高性能。根据预期的并发用户数量，配置一个线程池，设置好线程池里的线程数量的最小和最大值以及增长的最小和最大值。起初，servlet引擎创建一个线程数与配置中的最小线程数量相等的线程池。然后servlet引擎把池中的一个线程指派给一个请求而不是每次都创建新的线程，完成操作之后，servlet引擎把线程放回到线程池中。使用线程池，性能可以显著地提高。如果需要，根据线程的最大数和增长数，可以创建更多的线程。</p><p>　　技术7：选择正确的包括机制</p><p>　　在JSP页面中，有两中方式可以包括文件：包括指令(＜<a href="mailto:%@includefile=&quot;test.jsp&quot;%"><font color="#000000">%@includefile="test.jsp"%</font></a>＞)和包括动作(＜jsp:includepage="test.jsp"flush="true"/＞)。包括指令在编译阶段包括一个指定文件的内容；例如，当一个页面编译成一个servlet时。包括动作是指在请求阶段包括文件内容；例如，当一个用户请求一个页面时。包括指令要比包括动作快些。因此除非被包括的文件经常变动，否则使用包括指令将会获得更好的性能。</p><p>　　技术8：在useBean动作中使用合适的范围</p><p>　　使用JSP页面最强大方式之一是和JavaBean组件协同工作。JavaBean使用＜jsp:useBean＞标签可以嵌入到JSP页面中。语法如下：</p><p>＜jsp:useBeanid="name"scope="page|request|session|application"class=<br />"package.className"type="typeName"＞<br />＜/jsp:useBean＞ </p><p>　　scope属性说明了bean的可见范围。scope属性的默认值是page。你应该根据你应用的需求选择正确的范围，否则它将影响应用的性能。</p><p>　　例如，如果你需要一个专用于某些请求的对象，但是你把范围设置成了session，那么那个对象将在请求结束之后还保留在内存中。它将一直保留在内存中除非你明确地把它从内存中删除、使session无效或session超时。如果你没有选择正确的范围属性，由于内存和垃圾收集的开销将会影响性能。因此为对象设置合适的范围并在用完它们之后立即删除。</p><p>　　杂项技术</p><p>　　1) 避免字符串连接：由于String对象是不可变对象，使用“＋”操作符将会导致创建大量的零时对象。你使用的“＋”越多，产出的零时对象就越多，这将影响性能。当你需要连接字符串时，使用StringBuffer替代“＋”操作。</p><p>　　2) 避免使用System.out.println：System.out.println同步处理磁盘输入/输出，这大大地降低了系统吞吐量。尽可能地避免使用System.out.println。尽管有很多成熟的调试工具可以用，但有时System.out.println为了跟踪、或调试的情况下依然很有用。你应该配置System.out.println仅在错误和调试阶段打开它。使用finalBoolean型的变量，当配置成false时，在编译阶段完成优化检查和执行跟踪输出。</p><p>　　3) ServletOutputStream与PrintWriter比较：由于字符输出流和把数据编码成字节，使用PrintWriter引入了小的性能开销。因此，PrintWriter应该用在所有的字符集都正确地转换做完之后。另一方面，当你知道你的servlet仅返回二进制数据，使用ServletOutputStream，因为servlet容器不编码二进制数据，这样你就能消除字符集转换开销。</p><p>　　总结</p><p>　　本文的目的是展示给你一些实践的和已经证实的用于提高servlet和JSP性能的性能优化技术，这些将提高你的J2EE应用的整体性能。下一步应该观察其他相关技术的性能调整，如EJB、JMS和JDBC等</p></div>
<img src ="http://www.blogjava.net/zdygis/aggbug/67272.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zdygis/" target="_blank">zdygis</a> 2006-09-02 14:36 <a href="http://www.blogjava.net/zdygis/articles/67272.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>J2EE性能测试与优化（转载）</title><link>http://www.blogjava.net/zdygis/articles/67271.html</link><dc:creator>zdygis</dc:creator><author>zdygis</author><pubDate>Sat, 02 Sep 2006 06:32:00 GMT</pubDate><guid>http://www.blogjava.net/zdygis/articles/67271.html</guid><wfw:comment>http://www.blogjava.net/zdygis/comments/67271.html</wfw:comment><comments>http://www.blogjava.net/zdygis/articles/67271.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zdygis/comments/commentRss/67271.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zdygis/services/trackbacks/67271.html</trackback:ping><description><![CDATA[
		<p>本文的目的是说明如何在典型的J2EE环境下实现可伸缩性测试，性能测试和优化。 </p>
		<p><strong>定义</strong></p>
		<p>响应时间——从初始的请求到回应下载的完成（刷新整个网页）之间的时间。<br />负载——系统使用的尺度。当服务器的应用可以承受繁重的通讯量时被称为可以承受“高负载”。 <br />可伸缩性——可伸缩的应用应该拥有随负载线性增长的响应时间。这样的应用可以通过线性的（不是指数性的）增加硬件设施来处理越来越多的数据量。 <br />自动测试工具——通过模拟用户请求网页或在你的网站上遍历预编程的工作流（链接）的工具（如Segue软件的Silk，WebLoad等等）。 <br />负载测试工具——大多数自动测试工具同时也是负载测试工具，如WebLoad。这些工具可以模拟任意数量的用户使用你的站点，并且可以提供重要的分析数据如平均响应时间等。 <br />Profiler（程序刨析器）——Profiler是在你的应用程序运行时进行检测的程序。它能提供你有用的运行时刻信息如特殊代码块的运行时间，内存/堆利用率，内存中特殊对象的实例数目等等。 </p>
		<p><strong>性能测试的过程 </strong></p>
		<p>1） 功能测试。大多数应用程序的测试首先是对完成功能的测试。即确保应用程序中所有的测试用例/工作流正常工作。 <br />2） 负载和可伸缩性测试。负载和可伸缩性测试有两种方式：<br />☆ 在增加数据库数据量的同时测试响应时间。<br />☆ 在增加当前用户数量的同时测试响应时间。<br />3） 解释结果。在各种数据库容量和不同的负载下测试过响应时间后，就可以在这些测试的平均响应时间和测试中服务器的资源利用率的基础上来做出解释。<br />4） 优化。在上一步完成后识别出问题所在，然后解释结果并捕捉问题。</p>
		<p><strong>负载和可伸缩性测试</strong></p>
		<p>负载和可伸缩性测试的目的是确保你的应用程序在使用的高峰时期拥有较好的响应时间。你还可以测试你的应用程序在超载时的行为（因为你的站点的数据库中会有越来越多的数据）。在测试之前，先编写一些脚本，向数据库中填充平均数量的数据，运行性能测试，测量响应时间。然后向数据库中填充大量的数据（大约是三年内可预见数据量的三到四倍），再次运行性能测试。如果第二次测试的响应时间特别长，那么肯定有问题。</p>
		<p>在进行性能测试时，你可能会想模拟不同负载下服务器的使用情况。根据经验，可以模拟低负载（1-5并发用户）、中负载（10-50并发用户）、大负载（100并发用户）和重负载（1000以上并发用户）。注意这些数字是不确定的，根据你的商业需求而定。另外，使用负载测试软件模拟10个并发用户并不就真正代表了10个并发用户，因为在负载测试中每个“机器人”在再次命中服务器之前会等待几个毫秒。这样的话，使用负载测试工具模拟10个并发用户差不多代表实际网上冲浪中的30-40个用户。 </p>
		<p>在测试过了这三种负载级别后，就可以比较在这几种情况下的平均响应时间，考察你的服务器是否可承受大访问量，即平均响应时间是否线性增长。</p>
		<p><strong>解释结果 </strong></p>
		<p>性能测试过程中最有趣的就是解释负载测试的结果。让我们来看看几种不同的可能性： </p>
		<p>1．当数据库的数据量大量增长超过一定程度时，响应时间延长很多。</p>
		<p>当数据库中的记录从100行增加到50，000时，响应时间不应该有明显的增加。数据库索引技术使得从表中查找一条记录只需耗费几毫秒的时间，即使该表有成千上万条记录也一样。这就是说，从一个适度数据量的数据库增加到拥有超量数据的数据库时，如果响应时间大大延长，那么你可能还没有索引适当的列。</p>
		<p>2．负载增长时响应时间呈指数化增长</p>
		<p>如果在增加并发用户数量时系统变得不可用，那么你的系统就不是可伸缩的。这个结果的解释比较困难，因为问题原因可能是硬件、发布平台的配置、系统体系结构等等。一定要在测试过程中监控以下服务器资源：<br />☆ 监控内存请求。<br />☆ 监控CPU使用情况。 </p>
		<p>如果CPU是超负荷使用的，就需要更快的或更多的处理器。如果CPU利用率比较低，那么就可能是相关的输入输出问题，检查测试系统的数据库连接、线程数、网络配置等等。 </p>
		<p>检查过配置，确定不是硬件瓶颈，体系结构和代码都经过了优化，如果问题仍然存在的话，就该进行代码运行时监测了（使用profiler）。 </p>
		<p><strong>优化 </strong></p>
		<p>数据库、体系结构、配置和硬件都是需要优化的对象。上面已经提过，导致服务器负荷承受能力低的最简单的失误就是数据库没有经过优化。数据库管理员（DBA）对任何开发组来说都是至关重要的，如果没有的话，可以考虑这样做：</p>
		<p>察看所有的EJB，核实数据库没有执行任何编码的SQL语句的线性搜索。可以拷贝代码中的SQL语句到数据库的SQL查询工具窗口中，执行EXPLAIN子句：</p>
		<p>Explain select * from table where tablefield = somevalue </p>
		<p>尽管Explain的语法根据数据库的不同各有差异，但总有一些东西是相似的。数据库会告诉你该语句是根据索引查询还是线性查询。确保验证了数据库的每一个SQL语句都使用了索引。如果没有使用，建立索引。</p>
		<p>优化数据库后再优化硬件配置，下一步再使用profiler优化代码。</p>
		<p>Profiler程序在你的应用程序运行时分析它，并提供给你使用其它方法无法得到的信息，如： </p>
		<p>1．每个类在内存中有多少个对象和垃圾收集器的行为。</p>
		<p>☆ 这些信息可以帮助你识别那些类应该被缓存。<br />☆ 可以帮助你调整Java内存堆。</p>
		<p>2． 应用程序在具体的类中花费了多少时间。</p>
		<p>这是非常重要的特征。Profiler可以指出那个类是瓶颈。</p>
		<p>有一个这样的profiler程序叫做Optimize-It。Optimize-It可以用于任何Java程序或基于Java的应用服务器。它可以很容易的和WebLogic配置到一起，也可以用来刨析远程服务器上的应用程序。</p>
		<p>优化体系结构和具体的项目密不可分。不过其中有一些小的技巧： </p>
		<p>⑴．确保网络调用最少，特别是数据库调用。 <br />　　　☆ 一个大的数据库调用比很多小的调用要好得多。 <br />　　　☆ 确保ejbStore没有为只读操作保存任何信息。 <br />　　　☆ 使用Details Objects获取Entity Bean状态。</p>
		<p>　　⑵．确保在尽可能的情况下利用Cache。 <br />　　应用服务器可能允许在内存中使用Entity Bean缓存，确保利用了这些特征，因为它可以显著减少数据库调用并加速数据存取。 </p>
		<p>　　⑶．确保使用Session Bean作为Entity Bean的封装。<br />　　可以把一个完整的实例的工作流封装到一个网络调用中，作为一个Session Bean（和一个事务）的一个方法。 </p>
		<p>　　<strong>结论</strong><br /><br />　　应用程序的性能测试与优化是一个挑战。幸运的是，市场上有很多工具可以使这个过程变得简单。使用这些工具按照本文提到的简单步骤，你可以有效的捕获系统的瓶颈所在。</p>
<img src ="http://www.blogjava.net/zdygis/aggbug/67271.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zdygis/" target="_blank">zdygis</a> 2006-09-02 14:32 <a href="http://www.blogjava.net/zdygis/articles/67271.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>J2EE性能调优（转载）</title><link>http://www.blogjava.net/zdygis/articles/67270.html</link><dc:creator>zdygis</dc:creator><author>zdygis</author><pubDate>Sat, 02 Sep 2006 06:31:00 GMT</pubDate><guid>http://www.blogjava.net/zdygis/articles/67270.html</guid><wfw:comment>http://www.blogjava.net/zdygis/comments/67270.html</wfw:comment><comments>http://www.blogjava.net/zdygis/articles/67270.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zdygis/comments/commentRss/67270.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zdygis/services/trackbacks/67270.html</trackback:ping><description><![CDATA[
		<p>性能问题的最明显表现是网页的响应时间变慢。在J2EE系统中，经常体现有下面更为基本的症状：</p>
		<li>应用服务器资源的使用情况 
</li>
		<li>JVM堆的使用情况 
</li>
		<li>系统资源的使用情况 
</li>
		<li>数据库资源的使用情况 
</li>
		<li>网络活动 
<p>        这些现象表明J2EE应用依赖很多外部资源，并且是运行在一个层次化的执行模式的环境中：</p><img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.innovatedigital.com/publicimg/sp6-1.gif" width="188" onload="javascript:if(this.width&gt;500)this.style.width=500;" /><br /><p>        由于Java虚拟机和应用服务器掩盖了操作系统和硬件的特性，所以在设计软件系统时，架构工程师更应该深刻理解整个操作环境。 </p><p>        在设计软件系统时，架构工程师应把性能和可扩展性放在首位，然后开始寻找容易解决的问题，反应时间缓慢通常的原因是访问数据库效率低和过多地调用远程对象和方法。接下来，架构工程师可继续寻找不明显的原因，例如算法的累积影响和不必要的开销。</p><p>        现在市场上的各个J2EE应用服务器有很多配置项目。这里只简单介绍一些常见的性能优化配置项目。</p><p>        很多应用服务器都有一些与J2EE规范有关的操作系统配置项目或非标准的特性，这可以提高系统性能。应该化时间来理解这些性能配置。</p><h3>Java虚拟机堆和垃圾回收设置</h3><p>        任何Java应用的性能调整基础都涉及到堆的大小和垃圾回收设置。（这里主要讨论Sun HotSpor JVM）.</p><p>        堆可分为三代，年轻的（新的），年老的和持久的。Hotspot JVM的内存基本配置包括最大堆大小，初始堆大小和年轻一代堆的大小。当配置最大堆大小时可参考下面一些指导：</p></li>
		<li>最大大小应小于物理内存，避免虚存的页面调度。 
</li>
		<li>需要减去其他进程使用的内存 
</li>
		<li>在负载测试时进行优化 
<p>        注意不要将最大堆大小设置得过大。堆越大，内存中保存的对象越多。内存中对象越多，回收过程时间越长。</p><p>        配置初试堆大小的一般性策略包括： 
</p><h3></h3><p></p></li>
		<li>将初始大小设置为最大堆大小 
</li>
		<li>将初始大小设置为最大堆大小的1/4到1/2 
<p>        对于年轻一代堆大小，Sun 推荐是设置为最大堆大小的1/3。</p><p>        也可以选择不同的垃圾回收算法。首先是增量垃圾回收。该算法的意思是减少单个对象回收停顿时间，这样的结果是整体回收性能的下降。该算法将相互引用的对象分组，然后尝试按组回收。尝试回收的部分越小，回收处理的时间往往会越少。</p><p>        1.4.1版的HotSpot JVM增加了两个垃圾回收算法：并行算法和并发算法。</p><p>        在年轻一代堆中实现了并行算法。在多处理器的机器上，这种回收算法使用了多线程来提高性能。虽然这个算法会暂停所有的应用线程，但是由于利用了多个CPU使得回收时间非常快。在年轻一代堆中，该算法显著地减少了回收带来的停顿。</p><p>        在年老一代堆中实现了并发算法。在应用中最大限度地执行并发。回收过程分为4个阶段，覆盖了可回收对象的标记和清除操作。前两个过程会暂停应用线程，后两阶段可与应用并发执行。并发垃圾回收算法的"最大限度并发"特点可以使JVM利用更大的堆和多个CPU。因此应关注由于采用缺省的mark-compact(标记-压缩)和stop-the-world（停顿所有处理）等垃圾回收算法所带来的延迟和吞吐量问题。</p><p></p><h3>处理线程</h3><p></p><p>        J2EE应用服务器是多线程的应用。应用服务器的线程是一种资源池，处理请求和和应用服务器的内部功能等任务共享这些资源。</p><p>        很多应用服务器允许为特定的任务或应用配置不同大小的线程池。通常需要增加这些线程池的大小以满足应用负载的需要。</p><p>        架构工程师应该避免将线程池大小设置过大，这是因为会增加上下文交换的次数，从而降低应用的性能。线程池的大小通常应该能最大利用机器上的CPU，同时又不能使CPU过载。</p><p></p><h3>EJB配置项目</h3><p></p><p>         在应用服务器中，很多不同类型的EJB是以资源池的方式实现的。通常这些池大小和初始Bean的数量会明显影响应用的性能。</p><p>        架构工程师应该避免将这些池大小设置的过大，这样会导致不必要地消耗JVM和操作系统内存。另外，将初始Bean数量设置过高会使得应用服务器的启动时间长的难以接受。</p><p>        在应用服务器中，缓存很多不同类型的EJB。缓存大小和超时设置通常也会对应用性能带来显著影响。</p><p>         架构工程师应该避免将缓寸大小设置过大，这同样会不必要地消耗大量JVM和操作系统内存。此外，应避免设置过长的超时--例如当EJB不用时，仍被缓存---，这也会导致不必要地消耗大量内存。</p><p></p><h3>数据库配置项目</h3><p></p><p>        J2EE规范要求应用服务器厂商必须提供数据库连接资源池功能。通常增加数据库连接池的大小会提高性能。架构工程师应该考虑不同类型的SQL操作（例如事务型和批处理型）应使用不同的连接池。如果一个消息Bean执行批处理操作，那么应该为此另创建一个连接池，而不要与事务型操作使用同一个连接池。</p><p>        很多J2EE应用服务器提供了Prepared Statement 的缓存功能。创建Prepared Statement是很耗费资源的。在事务型的J2EE应用中通常执行很多同样的SQL语句，只是参数不同而已。所以在应用中应发挥数据库配置项目的作用，尽量使用Prepared Statement。</p></li>
		<li>
				<p> </p>
		</li>
		<li>
				<p>好的开始是成功的一半。对于J2EE同样如此，我们知道当开发应用时，在架构设计阶段的决定将对应用的性能和可扩展性产生深远的影响。</p>
				<p>        现在当开发一个应用项目时，我们越来越多地注意到了性能和可扩展性的问题。应用性能的问题比应用功能的不丰富问题往往更为严重，前者会影响到所有用户，而后者只会影响到碰巧使用该功能的那些用户。</p>
				<p>        作为应用系统的负责人，一直被要求"要少花钱多办事"----用更少的硬件，更少的网络带宽，以及更短的时间完成更多的任务。J2EE通过提供组件方式和通用的中间件服务是目前首选的最优方式。而要能够构建一个具有高性能和可扩展性的J2EE应用，需要遵循一些基本的架构策略。 </p>
				<p>
				</p>
				<h3>缓存(Caching)：</h3>
				<p>
				</p>
				<p>        简单地说，缓存中存放着频繁访问的数据，在应用的整个生命周期中，这些数据存放在持久性存储器或存放在内存中。在实际环境中，典型的现象是在分布式系统中每个JVM中有一个缓存的实例或者在多个JVM中有一个缓存的实例。 </p>
				<p>        缓存数据是通过避免访问持久性存储器来提高性能的，否则会导致过多的磁盘访问和过于频繁网络数据传输。</p>
				<p>
				</p>
				<h3>复制：</h3>
				<p>
				</p>
				<p>        复制是通过在多台物理机器上创建指定应用服务的多个拷贝来获得整体更大吞吐效率。理论上看，如果一个服务被复制成两个服务，那么系统将可处理两倍的请求。</p>
				<p>        复制是通过单一服务的多个实例的方式从而减少每个服务的负载来提高性能的。</p>
				<p>
				</p>
				<h3>并行处理</h3>
				<p>
				</p>
				<p>        并行处理将一个任务分解为更为简单的子任务，并能够同时在不同的线程中执行。</p>
				<p>        并行处理是通过利用J2EE层执行模式的多线程和多CPU特点来提高性能。与使用一个线程或CPU处理任务相比，以并行方式处理多个子任务可以使操作系统在多个线程或处理器中进行分配这些子任务。</p>
				<p>
				</p>
				<h3>异步处理</h3>
				<p>
				</p>
				<p>        应用功能通常被设计为同步或串行方式。异步处理只处理那些非常重要的任务部分，然后将控制立即返回给调用者，其他任务部分将在稍后执行。</p>
				<p>        异步处理是通过缩短那些在将控制返回给用户之前必须处理的时间来提高性能的。虽然都做同样多的事情，但是用户不必等到整个过程完成就可以继续发出请求了。</p>
				<p>
				</p>
				<h3>资源池</h3>
				<p>
				</p>
				<p>        资源池技术使用的是一套准备好的资源。与在请求和资源之间维持1：1的关系的不同，这些资源可被所有请求所共享。</p>
				<p>        资源池的使用是有条件的，需要衡量下面两种方式的代价：</p>
				<p>        A,维持一套可被所有请求共享资源的代价</p>
				<p>        B,为每个请求都重新创建一个资源的代价</p>
				<p>        当前者小于后者时，使用资源池才是有效率的。</p>
		</li>
		<br />
		<p>        构建高性能的J2EE应用不但需要了解常用的实施技巧。下面介绍最常用的10种有效方法，可帮助架构设计师们快速成为这方面的专家。</p>
		<p>
		</p>
		<h3>Java性能的基础----内存管理</h3>
		<p>
		</p>
		<p>        任何Java应用，单机的或J2EE的性能基础都可归结到你的应用是如何管理内存的问题。Java的内存管理包括两个重要任务：内存的分配和内存的回收。在内存的分配中，目标是要减少需要创建的对象。 内存回收是导致性能下降的普遍原因。也就是说，内存中的对象越多，垃圾回收越困难。所以我们对创建对象的态度应该越保守越好。 </p>
		<p>        在J2EE应用中常见的两个内存有关的问题是：游离的对象（也被称为内存泄露）和对象循环（指大量频繁创建和删除-在Java中体现为解除引用---对象）。</p>
		<p>        我们应注意确保所有可到达的对象实际是活的，即这些对象不但在内存中，而且也要在执行的代码中是存在的。当对象在应用中已经没有用了，而我们却忘记了删除对该对象的引用时，游离的对象就出现了。 </p>
		<p>        我们知道垃圾回收会占用CPU时间。短期对象的大量创建增加了垃圾回收的频率会造成性能下降。</p>
		<p>
		</p>
		<h3>不要在Servlet中实现业务逻辑</h3>
		<p>
		</p>
		<p>        在构建J2EE应用时，架构工程师通常会使用到J2EE的基本部分，Servlet。</p>
		<p>        如果架构师不使用Session Beans, Entity Beans, 或 Message Beans, 那么改进性能的方法就很少。只能采用增加CPU或更多的物理服务器等方法。EJB使用了缓存（cache）和资源池等方法可以提高性能和扩展性。</p>
		<p>
		</p>
		<h3>尽可能使用本地接口访问EJB</h3>
		<p>
		</p>
		<p>        在早期的J2EE （遵循EJB1.X规范）应用中，访问EJB是`通过RMI使用远程接口实现的。随着EJB2.0的出现，可以通过本地接口访问EJB，不再使用RMI，在同一个JVM中使用远程方法已经少多了。但是现在还是有一些使用EJB1.X实现的应用和不知道使用本地接口的一些EJB新手。为说明这点，我们作个比较：</p>
		<p>        1. 客户端应用调用本地Stub</p>
		<p>        2. 该Stub装配参数</p>
		<p>        3. 该Stub传到skeleton</p>
		<p>        4. 该skeleton分解参数</p>
		<p>        5. 该skeleton调用EJB对象</p>
		<p>        6. EJB对象执行容器服务</p>
		<p>        7. EJB对象调用企业BEAN实例</p>
		<p>        8. 企业BEA执行操作</p>
		<p>        9. 执行组装/分解步骤然后返回</p>
		<p>        与远程接口处理相比较，本地接口的EJB方法是：</p>
		<p>                1. 客户端调用本地对象</p>
		<p>                2. 本地对象执行容器服务</p>
		<p>                3. 本地对象调用企业Bean实例</p>
		<p>                4. 企业Bean实例执行操作</p>
		<p>                5. 没有其他返回步骤！！</p>
		<p>        如果你不需要从远程的客户端访问一个特殊EJB，就应该使用本地方法。</p>
		<p>
		</p>
		<h3>在实现Session Bean的服务中封装对实体EJB的访问</h3>
		<p>
		</p>
		<p>        从Servlet访问实体EJB不但效率低而且难于维护。使用Session Fa?ade（会话外观）模式可把对实体EJB的访问封装在会话EJB中，在该会话EJB中通过使用本地接口访问实体EJB而避免过多的远程调用。</p>
		<p>        这项技术会有额外的性能和扩展方面的好处，这是因为会话和实体EJB可以使用缓存和资源池技术来进行改进。另外，由于负载的需要，会话和实体EJB可被扩展部署到其他硬件设备上，这比将Servlet层复制扩展到其他硬件设备上要简单的多。</p>
		<p>
		</p>
		<h3>尽量粗粒度访问远程EJB</h3>
		<p>
		</p>
		<p>         当访问远程EJB时，调用set/get方法将产生过多的网络请求，同时也导致远程接口处理的过载。为避免这种情况，可考虑将数据属性集中在一个对象中，这样通过一次对远程EJB的调用就可以传递所有数据。这项技术就是数据传输对象（Data Transfer Object）模式。</p>
		<p>
		</p>
		<h3>优化SQL</h3>
		<p>
		</p>
		<p>        J2EE的架构设计工程师和开发人员通常不是SQL专家或经验丰富的数据库管理员。首先应该确保SQL使用了数据库提供的索引支持。在某些情况下，将数据库的索引和数据分开存放会提高性能。但要知道，增加额外的索引可以提高SELECT性能但也会降低INSERT的性能。对于某些数据库，关联表之间的排序会严重影响性能。可以多向数据库管理员咨询。</p>
		<p>
		</p>
		<h3>避免在实体EJB中过多执行SQL</h3>
		<p>
		</p>
		<p>        有时候，通过实体EJB访问数据会执行多个SQL语句。根据J2EE 规范，第一步，将调用实体Bean的find(发现)方法；第二步，在第一次调用实体EJB的业务方法时，容器会调用ejbLoad()从数据库中获得信息。</p>
		<p>        很多CMP(容器管理持久性)在调用发现方法时就缓存了实体数据，所以在调用ejbLoad()时就不再访问数据库了。应该避免使用BMP(Bean管理的持久性)或者自己实现缓存算法避免二次访问数据库。</p>
		<p>
		</p>
		<h3>使用Fast Lane Reader 模式访问只读数据</h3>
		<p>
		</p>
		<p>        J2EE应用经常要以只读方式访问大量长时间不变的数据，而不是访问单个实体，例如浏览在线产品目录。在这种只读情况下，使用实体EJB访问数据会导致严重过载并且实现很麻烦。实体EJB 适合于对单个实体的粗粒度访问，访问大量的列表只读数据时效率不高。不管是使用CMP还是BMP，一定需要编写代码操作多个实体EJB及其关联。这将导致访问多个数据库并存在大量的也是不必要的事务开销。</p>
		<p>
		</p>
		<h3>利用Java Messaging Servce(消息服务)</h3>
		<p>
		</p>
		<p>        J2EE规范在JMS中提供了内置的异步处理服务。当涉及到系统需求时，应该了解在什么情况下应该采用JMS进行异步处理的设计。一旦确定要执行一些异步处理，那么同步处理的任务就应该越少越好，将数据库密集的操作安排在稍后的异步处理中完成。</p>
		<p>
		</p>
		<h3>缓存JNDI Lookup查找</h3>
		<p>
		</p>
		<p>        很多操作在进行JNDI查找时要消耗大量资源。通常应该缓存JNDI资源避免网络调用和某些处理的过载。可以缓存的JNDI查找包括：</p>
		<p>        EJB Home Interfaces</p>
		<p>        Data Sources</p>
		<p>        JMS Connection Factories</p>
		<p>        JMS Destinations/Topics</p>
		<p>        一些JNDI包实现了缓存功能。但是调用对EJB主接口的narrow方法时，这种功能作用有限。</p>
		<p>        缓存查找的设计应该使用共享的IntialContext实例，尽管构建它很麻烦。这是因为需要访问多种数据源，包括应用资源文件JNDI.properties,系统属性的各项参数,传入到构造函数的各项参数。</p>
<img src ="http://www.blogjava.net/zdygis/aggbug/67270.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zdygis/" target="_blank">zdygis</a> 2006-09-02 14:31 <a href="http://www.blogjava.net/zdygis/articles/67270.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java性能优化技巧J2EE篇(转载)</title><link>http://www.blogjava.net/zdygis/articles/67269.html</link><dc:creator>zdygis</dc:creator><author>zdygis</author><pubDate>Sat, 02 Sep 2006 06:25:00 GMT</pubDate><guid>http://www.blogjava.net/zdygis/articles/67269.html</guid><wfw:comment>http://www.blogjava.net/zdygis/comments/67269.html</wfw:comment><comments>http://www.blogjava.net/zdygis/articles/67269.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zdygis/comments/commentRss/67269.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zdygis/services/trackbacks/67269.html</trackback:ping><description><![CDATA[
		<span class="Contents">
				<span class="px14">
						<strong>J2EE篇<br /></strong>
						<p>    前面介绍的改善性能技巧适合于大多数Java应用，接下来要讨论的问题适合于使用JSP、EJB或JDBC的应用。<br /></p>
						<p>    <b>2.1 使用缓冲标记</b><br /></p>
						<p>    一些应用服务器加入了面向JSP的缓冲标记功能。例如，BEA的WebLogic Server从6.0版本开始支持这个功能，Open Symphony工程也同样支持这个功能。JSP缓冲标记既能够缓冲页面片断，也能够缓冲整个页面。当JSP页面执行时，如果目标片断已经在缓冲之中，则生成该片断的代码就不用再执行。页面级缓冲捕获对指定URL的请求，并缓冲整个结果页面。对于购物篮、目录以及门户网站的主页来说，这个功能极其有用。对于这类应用，页面级缓冲能够保存页面执行的结果，供后继请求使用。<br /></p>
						<p>    对于代码逻辑复杂的页面，利用缓冲标记提高性能的效果比较明显；反之，效果可能略逊一筹。<br /></p>
						<p>    <b>2.2 始终通过会话Bean访问实体Bean</b><br /></p>
						<p>    直接访问实体Bean不利于性能。当客户程序远程访问实体Bean时，每一个get方法都是一个远程调用。访问实体Bean的会话Bean是本地的，能够把所有数据组织成一个结构，然后返回它的值。<br /></p>
						<p>    用会话Bean封装对实体Bean的访问能够改进事务管理，因为会话Bean只有在到达事务边界时才会提交。每一个对get方法的直接调用产生一个事务，容器将在每一个实体Bean的事务之后执行一个“装入-读取”操作。<br /></p>
						<p>    一些时候，使用实体Bean会导致程序性能不佳。如果实体Bean的惟一用途就是提取和更新数据，改成在会话Bean之内利用JDBC访问数据库可以得到更好的性能。<br /></p>
						<p>    <b>2.3 选择合适的引用机制</b><br /></p>
						<p>    在典型的JSP应用系统中，页头、页脚部分往往被抽取出来，然后根据需要引入页头、页脚。当前，在JSP页面中引入外部资源的方法主要有两种：include指令，以及include动作。<br /></p>
						<ul>
								<li>include指令：例如&lt;%@ include file="copyright.html" %&gt;。该指令在编译时引入指定的资源。在编译之前，带有include指令的页面和指定的资源被合并成一个文件。被引用的外部资源在编译时就确定，比运行时才确定资源更高效。<br /></li>
								<li>include动作：例如&lt;jsp:include page="copyright.jsp" /&gt;。该动作引入指定页面执行后生成的结果。由于它在运行时完成，因此对输出结果的控制更加灵活。但时，只有当被引用的内容频繁地改变时，或者在对主页面的请求没有出现之前，被引用的页面无法确定时，使用include动作才合算。</li>
						</ul>
						<br />
						<p>    <b>2.4 在部署描述器中设置只读属性</b><br /></p>
						<p>    实体Bean的部署描述器允许把所有get方法设置成“只读”。当某个事务单元的工作只包含执行读取操作的方法时，设置只读属性有利于提高性能，因为容器不必再执行存储操作。<br /></p>
						<p>    <b>2.5 缓冲对EJB Home的访问</b><br /></p>
						<p>    EJB Home接口通过JNDI名称查找获得。这个操作需要相当可观的开销。JNDI查找最好放入Servlet的init()方法里面。如果应用中多处频繁地出现EJB访问，最好创建一个EJBHomeCache类。EJBHomeCache类一般应该作为singleton实现。<br /></p>
						<p>    <b>2.6 为EJB实现本地接口</b><br /></p>
						<p>    本地接口是EJB 2.0规范新增的内容，它使得Bean能够避免远程调用的开销。请考虑下面的代码。<br /></p>
						<p>     
</p>
						<table cellspacing="0" bordercolordark="#ffffff" cellpadding="5" width="80%" bgcolor="#dbdbdb" bordercolorlight="black" border="1">
								<tbody>
										<tr>
												<td>
														<pre>
																<p>
																		<br />PayBeanHome home = (PayBeanHome) <br />         javax.rmi.PortableRemoteObject.narrow <br />         (ctx.lookup ("PayBeanHome"), PayBeanHome.class); <br />PayBean bean = (PayBean) <br />         javax.rmi.PortableRemoteObject.narrow <br />         (home.create(), PayBean.class); <br /></p>
														</pre>
												</td>
										</tr>
								</tbody>
						</table>
						<br />
						<br />
						<p>    第一个语句表示我们要寻找Bean的Home接口。这个查找通过JNDI进行，它是一个RMI调用。然后，我们定位远程对象，返回代理引用，这也是一个RMI调用。第二个语句示范了如何创建一个实例，涉及了创建IIOP请求并在网络上传输请求的stub程序，它也是一个RMI调用。<br /></p>
						<p>    要实现本地接口，我们必须作如下修改：<br /></p>
						<ul>
								<li>方法不能再抛出java.rmi.RemoteException异常，包括从RemoteException派生的异常，比如TransactionRequiredException、TransactionRolledBackException和NoSuchObjectException。EJB提供了等价的本地异常，如TransactionRequiredLocalException、TransactionRolledBackLocalException和NoSuchObjectLocalException。<br /></li>
								<li>所有数据和返回值都通过引用的方式传递，而不是传递值。<br /></li>
								<li>本地接口必须在EJB部署的机器上使用。简而言之，客户程序和提供服务的组件必须在同一个JVM上运行。<br /></li>
								<li>如果Bean实现了本地接口，则其引用不可串行化。</li>
						</ul>
						<br />
						<p>    <b>2.7 生成主键</b><br /></p>
						<p>    在EJB之内生成主键有许多途径，下面分析了几种常见的办法以及它们的特点。<br /></p>
						<p>    利用数据库内建的标识机制（SQL Server的IDENTITY或Oracle的SEQUENCE）。这种方法的缺点是EJB可移植性差。<br /></p>
						<p>    由实体Bean自己计算主键值（比如做增量操作）。它的缺点是要求事务可串行化，而且速度也较慢。<br /></p>
						<p>    利用NTP之类的时钟服务。这要求有面向特定平台的本地代码，从而把Bean固定到了特定的OS之上。另外，它还导致了这样一种可能，即在多CPU的服务器上，同一个毫秒之内生成了两个主键。<br /></p>
						<p>    借鉴Microsoft的思路，在Bean中创建一个GUID。然而，如果不求助于JNI，Java不能确定网卡的MAC地址；如果使用JNI，则程序就要依赖于特定的OS。<br /></p>
						<p>    还有其他几种办法，但这些办法同样都有各自的局限。似乎只有一个答案比较理想：结合运用RMI和JNDI。先通过RMI注册把RMI远程对象绑定到JNDI树。客户程序通过JNDI进行查找。下面是一个例子：<br /></p>
						<p>     
</p>
						<table cellspacing="0" bordercolordark="#ffffff" cellpadding="5" width="80%" bgcolor="#dbdbdb" bordercolorlight="black" border="1">
								<tbody>
										<tr>
												<td>
														<pre>
																<p>
																		<br />public class keyGenerator extends UnicastRemoteObject implements Remote { <br />private static long KeyValue = System.currentTimeMillis(); <br />public static synchronized long getKey() throws RemoteException { return KeyValue++; } <br /></p>
														</pre>
												</td>
										</tr>
								</tbody>
						</table>
						<br />
						<br />
						<p>    <b>2.8 及时清除不再需要的会话</b><br /></p>
						<p>    为了清除不再活动的会话，许多应用服务器都有默认的会话超时时间，一般为30分钟。当应用服务器需要保存更多会话时，如果内存容量不足，操作系统会把部分内存数据转移到磁盘，应用服务器也可能根据“最近最频繁使用”（Most Recently Used）算法把部分不活跃的会话转储到磁盘，甚至可能抛出“内存不足”异常。在大规模系统中，串行化会话的代价是很昂贵的。当会话不再需要时，应当及时调用HttpSession.invalidate()方法清除会话。HttpSession.invalidate()方法通常可以在应用的退出页面调用。<br /></p>
						<p>    <b>2.9 在JSP页面中关闭无用的会话</b><br /></p>
						<p>    对于那些无需跟踪会话状态的页面，关闭自动创建的会话可以节省一些资源。使用如下page指令：<br /></p>
						<p>     
</p>
						<table cellspacing="0" bordercolordark="#ffffff" cellpadding="5" width="80%" bgcolor="#dbdbdb" bordercolorlight="black" border="1">
								<tbody>
										<tr>
												<td>
														<pre>
																<p>
																		<br />&lt;%@ page session="false"%&gt; <br /></p>
														</pre>
												</td>
										</tr>
								</tbody>
						</table>
						<br />
						<br />
						<br />
						<p>    <b>2.10 Servlet与内存使用</b><br /></p>
						<p>    许多开发者随意地把大量信息保存到用户会话之中。一些时候，保存在会话中的对象没有及时地被垃圾回收机制回收。从性能上看，典型的症状是用户感到系统周期性地变慢，却又不能把原因归于任何一个具体的组件。如果监视JVM的堆空间，它的表现是内存占用不正常地大起大落。<br /></p>
						<p>    解决这类内存问题主要有二种办法。第一种办法是，在所有作用范围为会话的Bean中实现HttpSessionBindingListener接口。这样，只要实现valueUnbound()方法，就可以显式地释放Bean使用的资源。<br /></p>
						<p>    另外一种办法就是尽快地把会话作废。大多数应用服务器都有设置会话作废间隔时间的选项。另外，也可以用编程的方式调用会话的setMaxInactiveInterval()方法，该方法用来设定在作废会话之前，Servlet容器允许的客户请求的最大间隔时间，以秒计。<br /></p>
						<p>    <b>2.11 HTTP Keep-Alive</b><br /></p>
						<p>    Keep-Alive功能使客户端到服务器端的连接持续有效，当出现对服务器的后继请求时，Keep-Alive功能避免了建立或者重新建立连接。市场上的大部分Web服务器，包括iPlanet、IIS和Apache，都支持HTTP Keep-Alive。对于提供静态内容的网站来说，这个功能通常很有用。但是，对于负担较重的网站来说，这里存在另外一个问题：虽然为客户保留打开的连接有一定的好处，但它同样影响了性能，因为在处理暂停期间，本来可以释放的资源仍旧被占用。当Web服务器和应用服务器在同一台机器上运行时，Keep-Alive功能对资源利用的影响尤其突出。<br /></p>
						<p>    <b>2.12 JDBC与Unicode</b><br /></p>
						<p>    想必你已经了解一些使用JDBC时提高性能的措施，比如利用连接池、正确地选择存储过程和直接执行的SQL、从结果集删除多余的列、预先编译SQL语句，等等。<br /></p>
						<p>    除了这些显而易见的选择之外，另一个提高性能的好选择可能就是把所有的字符数据都保存为Unicode（代码页13488）。Java以Unicode形式处理所有数据，因此，数据库驱动程序不必再执行转换过程。但应该记住：如果采用这种方式，数据库会变得更大，因为每个Unicode字符需要2个字节存储空间。另外，如果有其他非Unicode的程序访问数据库，性能问题仍旧会出现，因为这时数据库驱动程序仍旧必须执行转换过程。<br /></p>
						<p>    <b>2.13 JDBC与I/O</b><br /></p>
						<p>    如果应用程序需要访问一个规模很大的数据集，则应当考虑使用块提取方式。默认情况下，JDBC每次提取32行数据。举例来说，假设我们要遍历一个5000行的记录集，JDBC必须调用数据库157次才能提取到全部数据。如果把块大小改成512，则调用数据库的次数将减少到10次。<br /></p>
						<p>    在一些情形下这种技术无效。例如，如果使用可滚动的记录集，或者在查询中指定了FOR UPDATE，则块操作方式不再有效。<br /></p>
						<p>    <b>2.14 内存数据库</b><br /></p>
						<p>    许多应用需要以用户为单位在会话对象中保存相当数量的数据，典型的应用如购物篮和目录等。由于这类数据可以按照行/列的形式组织，因此，许多应用创建了庞大的Vector或HashMap。在会话中保存这类数据极大地限制了应用的可伸缩性，因为服务器拥有的内存至少必须达到每个会话占用的内存数量乘以并发用户最大数量，它不仅使服务器价格昂贵，而且垃圾收集的时间间隔也可能延长到难以忍受的程度。<br /></p>
						<p>    一些人把购物篮/目录功能转移到数据库层，在一定程度上提高了可伸缩性。然而，把这部分功能放到数据库层也存在问题，且问题的根源与大多数关系数据库系统的体系结构有关。对于关系数据库来说，运行时的重要原则之一是确保所有的写入操作稳定、可靠，因而，所有的性能问题都与物理上把数据写入磁盘的能力有关。关系数据库力图减少I/O操作，特别是对于读操作，但实现该目标的主要途径只是执行一套实现缓冲机制的复杂算法，而这正是数据库层第一号性能瓶颈通常总是CPU的主要原因。<br /></p>
						<p>    一种替代传统关系数据库的方案是，使用在内存中运行的数据库（In-memory Database），例如TimesTen。内存数据库的出发点是允许数据临时地写入，但这些数据不必永久地保存到磁盘上，所有的操作都在内存中进行。这样，内存数据库不需要复杂的算法来减少I/O操作，而且可以采用比较简单的加锁机制，因而速度很快。</p>
				</span>
		</span>
<img src ="http://www.blogjava.net/zdygis/aggbug/67269.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zdygis/" target="_blank">zdygis</a> 2006-09-02 14:25 <a href="http://www.blogjava.net/zdygis/articles/67269.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>