使用ear部署提高项目性能

来源:互联网 发布:华为软件 什么部门好 编辑:程序博客网 时间:2024/04/28 15:57

公司有个项目在最近连续几天又报内存溢出的异常。我被指定协助项目组解决这个问题。内存溢出问题往往最难解决,从哪里下手呢?先从调研开始,与项目组的技术人员沟通之后了解到:

1、  受机器条件所限只能将jvm的最大内存设为2048M。所以只能从其它方面想办法。

2、  最近一年多在运行一段时间之后总是报内存溢出的错误。考虑过很多办法也没有从根本上解决这个问题,最后之好写了一个脚本,在每天晚上12点钟重新启动webLogic 用这种不是办法的办法解了燃眉之急。

3、  检查代码中静态变量的使用情况,特别是集合类型的静态变量,不过该项目代码量非常大,总共有10多个部署的war包,短时间内还是很难完成这项检查工作,而且这一年多来该项目组也对代码进行了很多的优化工作,他们认为静态变量的问题不大。

4、  项目组怀疑是因为部署的war包太多,每个war包中引入的jar包数量都在200M左右,10多个war包中的jar大小累加起来都超过2G了,他们认为是因为jar本身在装入时占用了太多的内存。

不过我的直觉告诉我,jar本身占用的内存应该与程序运行区分配的内存是无关的,因为以上编C++程序时就是这样的,程序区与数据区是分开的,程序区不是我们要考虑的。但是我猜测,如果某个jar中的缓存了一份内存,而且这个jar包又分别被这10多个war包引用的话,那么内存的占用量就会放大了10倍。不过我当时怀疑的是我们自己开发的公用jar包,结果花了很大的时间最找我们自己公用jar包中的静态变量的问题,不过收获不大。

后来使用ha28分析了三个heapdump文件样本,发现每个样本中BeanHelper占用的内存都在100M-400M之间

进一步分析发现,是因为BeanHelper下引用了beanutils.jar包中的对象占用的内存,如下图所示:

 

 

内存主要被org.apache.commons.beanutils.MappedPropertyDescriptor内中的一个Hashtable对象占用的

通过分析beanutils1.7.jar的源码发现,该类中只有一个Hashtable对象

private static java.util.Hashtable

        declaredMethodCache = new java.util.Hashtable();

该对象为静态变量

作用是用来将所有曾经通过Class.getDeclaredMethods();方法得到的Method[] methods数组都缓存起来,以减少Class.getDeclaredMethods()的调用次数以提高性能。该hashtable的键为javabean的class,值为Method[].

对于我们系统来说,我们保守的估计javaBean的数量为500个(因该不止这个数),每个bean的Method[]数据大小设为50K

那么每个war包中该hashtable占用的内存大小就占到了500*50=25M

又因为我们在部署时将beanutils1.7.jar包是放在war包里的web-inf/lib目录下的,以这种方式布置,declaredMethodCache这个hashtable将在每个war中产生一个静态实例,

因为我们系统在weblogic一个节点下部署了10多个war包,那将该对象将点用25M*10=250M的内存,单一个类的静态变量就占用了这么多内存,如果系统中存在几个这个的对象,系统的内存占用量就会很恐怖了。

解决方案一:

我查看了beanutils 1.8.0.jar的源码,可能apache也发现了这个问题,在1.8的源码中该静态对象已去掉了,改为每次都调用Class.getDeclaredMethods().

所以将beanutils1.7.0.jar包替换成beanutils1.8.0.jar即可解决这个问题,为系统节省200M以上的内存。

但是该解决方案只能具体的解决beanutils1.7.0.jar包带来的这一个问题,另外还有上百个开源的jar包中可能也或多或少的使用到缓存,积累下来对系统的性能影响还是很大的。

解决方案二:

将公共引用的jar包提取出来放到容量的classpath目录下,使用这些方式可以保存公用jar包的静态变理占用的内存只有一份,不过如果容器下如果还部署有其它系统,这些提取到公用classpath中的jar包可能会与其它系统引用的jar包产生冲突。

解决方案三:

使用ear包部署,将系统各war包公用的jar包提取到ear的lib目录中,就可以即解决内存占用量大的问题,又不会对同一容器下部署的其它系统产生影响。

         Java提供ear包部署方式来解决企业级应用的部署问题,不过在实现项目中被使用的不多,有很多开发人员甚至不了解ear包的作用,用些人认为只有使用ejb时才需要使用ear包部署。而且不象war包,不同容器对ear的支持方式也不尽相同,如:

weblogic对ear的支持是如下图所示:

   

将各war项目中需要使用到的共用jar包统一放到app-inf/lib目录下,将共用的classes统一放到app-info/classes目录下。

Jboss却是将公用的jar包放到ear包中的lib目录下,且不支持公用的class类;

    Websphere最为奇怪,它要求所有的web项目都要以ear的方式部署,如果你部署一个war包,它也会自动将该war包转化成一个ear包。不过websphere对ear标准的支持确并不好,was不会自动将ear下的公用jar包加载到classpath,而是需要定以一个环境变量,再将每一个公用jar包的路径定义到这个环境变量中。(感觉很傻,不过IBM的东西就是这样,所么都有就是不好用,was就是会经常出现一个小版本号不同而差别巨大,我就碰到过一次,在开发测试时一切没有问题,可是系统在生产机一上线确出大问题了,就是因为生产机上的小版本号与开发机上的不同,导至过滤器不正确及JDK的API反回结果不同,害得我奋战了两天才找出原因。见相关的博文)。

    其它象tomcat之类的轻量级的容器不支持ear。

   

    在很多大型项目中,因为功能模块多,开发人员多,往往需要将项目分解成多个web工程,以方便管理和部署及维护。然后在系统部署时每个web项目部署成一个war包,不过这种分解也会带来一个大问题,那就是现在web工程一般都会引有大量的开源jar包,如果每个war包中放一份jar包的话,因为每个war包web-inf目录下的jar包和class类都由一个独立的classloader导入,所以jar包本身都将会占用大量的内存。如果使用ear部署,只有一份公用的jar包,只需要被一个classloader导入,因此减少大量的内存。这只是一个方面,如果公用jar包内部使用了缓存技术的话,那么节省的内存就更可观了,甚至可以及大的提高系统的性能。

原创粉丝点击