实现有价值的IT服务
全部博文(709)
分类: Java
2006-08-21 16:52:20
但是Java的流行并不一定使之对于日益增长的Java代码开发人员变得容易。为了在生产上达到一个新的高度,程序员们逐渐在更大的团队中工作,而在他们当中,持续缩短开发周期始终很热门。每天,那些团队都要面对一个软件开发中的不变定律:您编写的代码越多,您就会碰到越多的bug——而且是浪费时间和降低应用程序质量和性能的bug。
本文主要介绍了一个J2EE应用程序的性能调优和内存使用优化。我们设置使用 BEA WebLogic Application Server。我们将讨论以下几个方面:
问题域
我们有一个如下设置的J2EE应用程序:
BEA WebLogic 6.1 Service Pack 5 作为应用/Web服务器。
某个流行的RDBMS。对我们的讨论没有影响。
Model I Web Architecture。
8个无状态EJB以及6个有状态EJB。
HTTP session拥有对有状态EJB的引用。
Database Connection Pool初始化为2,最大值为10。
在Web层有大约120个servlet。
基于XML/XSLT的体系结构。
问题
应用程序有一个内存问题。当服务器启动时,内存使用率大约占全部可用物理内存的7~8%。随着时间的推移和更多地使用应用程序,内存使用率会增加到接近49~53%(7~10天一个周期)。
如果用户通过点击左边菜单中的“Log off”按钮从他们的会话中注销,那么应用程序就会从服务器中删去所有的有状态bean。但是,如果一个用户仅仅只是关闭浏览器窗口,那它就不会删去那些 bean,而会在容器中保留它们直到应用服务器重新启动为止。这样继续下去,内存中的EJB实例数量会增加到400个甚至更多。
当BEA WebLogic Server装载了多于400个EJB时,Hotspot虚拟机会抛出一个OutOfMemory 异常。尽管看似还有更多内存可用,但这种情况还是会发生。
调优Java虚拟机
当试图分配PermGeneration空间时,Hotspot虚拟机会抛出OutOfMemory异常。Hotspot虚拟机使用不同部分的内存。持久生成部分被用来储存类、方法以及运行的Java对象所使用的符号。持久生成部分的初始大小为1MB,最大值为64MB,1.3.1之前是32MB。
要避免这种情况,我们可以通过一个Java虚拟机开关,使用下面的命令行来设置permGeneration空间。
java -server -XX:MaxPermSize=128M
注意,增加perm最大值只是推迟了故障的发生。最终还是要靠您的应用程序去适当地清除无用的对象。另外,并非所有的Java虚拟机都支持XX选项。
HTTP Session 管理
当用户没有从会话中注销就关闭浏览器时,用户会话中的EJB不会被垃圾收集。这就是内存中有太多EJB的主要原因。要避免这种情况,HTTP session管理必须注意所有可能的结合。我们可以在web.xml(Web应用程序部署描述符)中设置一个默认的会话超时周期,如下所示:
<session-config> <session-timeout>xsession-timeout> <session-config>
通过这个设置,用户的会话会在不活动x分钟后自动释放。
另一种方法是在创建HTTP session时用下面的代码编写会话管理
HttpSession session=new HttpSession (); session.setmaxinactiveinternal(int timeoutSeconds);
这段代码会使不活动了timeoutSeconds时间的用户会话失效。
注意:如果您把两个步骤都做了,那么在servlet代码中的值将覆盖在web.xml中设置的值。
这两种方法惟一的不同点是第二种方法以秒作为参数,而
javax.servlet.http.HTTPSessionListener接口
该接口声明了下面两种回调方法:
Public void sessionCreated(HttpSessionEvent event); Public void sessionDestroyed(HttpSessionEven event);
这些方法在一个会话被创建/销毁前被调用。
我们可以使用一个实现了这个接口的监听器类,并用这些回调方法来控制会话的创建和销毁。我们需要像下面这样在web.xml中注册我们的监听器类:
<listener> <listener-class>MySessionListenerlistener-class> listener>
使用监听器类以及在web.xml文件中添加会话超时参数的好处是我们能对会话管理进行更多的控制。如果会话拥有大对象,那么在垃圾收集器清除这些对象之前,它的时间片可能会消失。在这种情况下,就需要等到下一个时间片才能清除这些对象。
注意:我们所设计的应用程序只有一个进入点是很重要的。我们需要在这个类中启动一个新的HTTP session。所有余下的页面应该检查HTTP session是否存在,并且当会话为null时(Session 到期)调入一个错误页面。这样就可以实现对HTTP session的集中控制。
调优应用服务器
BEA WebLogic 提供了一些参数,我们能用它们优化bean池的大小,这包括设置无状态bean池的初始大小。以下是一些使用方法:
用
用
如果达到了max-beans-in-cache且高速缓存中的EJB并没有正在使用,WebLogic Server会把其中某些bean钝化。尽管没用的bean还没有达到它们的 idle-timeout-seconds极限,但这也会发生。如果达到了max-beans-in-cache且高速缓存中的EJB正在被客户端使用,那么WebLogic Server就会抛出一个CacheFullException异常。
用
例如,考虑下面的设置
<idle-timeout-seconds>1200idle-timeout-seconds>
这个空闲bean实例将会在静止20分钟后被钝化。再过20分钟,这个bean实例会从硬盘上删去。现在,让我们假设在第41分钟用户调用了这个bean实例的一个方法。BEA WebLogic Server将会抛出错误,如程序清单1所示。
程序清单 1:
Bean has been deleted. at weblogic.ejb20.swap.DiskSwap.read(DiskSwap.java:156) at weblogic.ejb20.manager.StatefulSessionManager.getBean(StatefulSession Manager.java:242) at weblogic.ejb20.manager.StatefulSessionManager.preInvoke(StatefulSessionManager.java:313) at weblogic.ejb20.internal.BaseEJBLocalObject.preInvoke(BaseEJBLocalObject.java:113) at weblogic.ejb20.internal.StatefulEJBLocalObject.preInvoke(StatefulEJBLocalObject.java:126)
WebLogic 6.1 Service Pack 5提供了一个有用的标记来避免发生这种情况。这个标记如下所示:
ELEMENT session-timeout-seconds (#PCDATA)>
如果我们为这个
使用WebLogic被管理服务器
BEA WebLogic 允许您在单独一个域中创建一个或多个服务器。一个服务器是管理服务器,所有其它服务器是被管理服务器,也就是被管理服务器管理。一个应用程序投入使用后,不应该被部署在管理服务器上。使用被管理服务器的好处是我们能从管理控制台上启动和停止它们。因此,即使服务器使应用程序停止响应(任何的原因),我们还是可以有机会从管理控制台重新启动服务器。
编码标准:为未来放弃规则
当系统的运行环境并没有在设计阶段考虑到时,管理一个开发系统会更加困难。当在设计阶段认真考虑了环境、边界、应用程序的运行环境后,系统再开发将是一项简单的任务。设计是执行路径的抽象定义。当考虑了设计中的每一个细节后再开发,解决方案会更具可伸缩性。对于系统开发并没有硬性的规定,因为这取决于您正面对的特定问题域。但是,还是有一些总会有帮助的定律。
对于补充类比如String和StringBuffer要有清晰的认识: 在合适的情况下用合适的类。例如,用String类构建一个长SQL查询是效率很低的,并加重JVM string池的负荷。在您使用完继承对象后立即清除它们,比如 Hashtable。在EJB引用中显式地调用 remove() 方法:这会把bean实例释放到bean池中,并减少由容器创建的bean实例数量。
小心地管理数据库连接:在您使用完后立即调用close()方法。不要在类的第一行就打开连接。而只在需要的时候才打开连接。
理解业务需求,并在您编写第一行代码之前就了解代码的执行环境。
总之一句话,”尽可能晚的创建对象而尽可能早的删除它们”。