java内存管理_第1页
java内存管理_第2页
java内存管理_第3页
java内存管理_第4页
java内存管理_第5页
已阅读5页,还剩34页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、java内存管理java做的系统给人的印象是什么?占内存!说道这句话就会有N多人站出来为java辩护,并举出一堆的性能测试报告来证明这一点。其实从理论上来讲 java做的系统并不比其他语言开发出来的系统更占用内存,那么为什么却有这么N多理由来证明它确实占内存呢?两个字,陋习。(1)别用new Boolean(。在很多场景中Boolean类型是必须的,比如JDBC中boolean类型的set与get都是通过Boolean封装传递的,大部分ORM也是用Boolean来封装boolean类型的,比如:ps.setBoolean("isClosed",new Boolean(tru

2、e;ps.setBoolean("isClosed",new Boolean(isClosed;ps.setBoolean("isClosed",new Boolean(i=3;通常这些系统中构造的Boolean实例的个数是相当多的,所以系统中充满了大量Boolean实例小对象,这是相当消耗内存的。Boolean类实际上只要两个实例就够了,一个true的实例,一个false的实例。Boolean类提供两了个静态变量:public static final Boolean TRUE = new Boolean(true;public static fina

3、l Boolean FALSE = new Boolean(false;需要的时候只要取这两个变量就可以了,比如:ps.setBoolean("isClosed",Boolean.TRUE;那么象2、3句那样要根据一个boolean变量来创建一个Boolean怎么办呢?可以使用Boolean提供的静态方法:   Boolean.valueOf()比如:ps.setBoolean("isClosed",Boolean.valueOf(isClosed;ps.setBoolean("isClosed",Boolean.

4、valueOf(i=3;因为valueOf的内部实现是:return (b ? TRUE : FALSE;所以可以节省大量内存。相信如果Java规范直接把Boolean的构造函数规定成private,就再也不会出现这种情况了。(2)别用new Integer。和 Boolean类似,java开发中使用Integer封装int的场合也非常多,并且通常用int表示的数值通常都非常小。SUN SDK中对Integer的实例化进行了优化,Integer类缓存了-128到127这256个状态的Integer,如果使用 Integer.valueOf(int i,传入的int范围正好在此内,就返回静态实例

5、。这样如果我们使用Integer.valueOf代替new Integer的话也将大大降低内存的占用。如果您的系统要在不同的SDK(比如IBM SDK)中使用的话,那么可以自己做了工具类封装一下,比如IntegerUtils.valueOf(,这样就可以在任何SDK中都可以使用这种特性。(3)用StringBuffer代替字符串相加。这个我就不多讲了,因为已经被人讲过N次了。我只想将一个不是笑话的笑话,我在看国内某“ 著名”java开发的WEB系统的源码中,竟然发现其中大量的使用字符串相加,一个拼装SQL语句的方法中竟然最多构造了将近100个string实例。无语中!(4)过滥使用哈希表,有一

6、定开发经验的开发人员经常会使用hash表(hash表在JDK中的一个实现就是HashMap)来缓存一些数据,从而提高系统的运行速度。比如使用HashMap缓存一些物料信息、人员信息等基础资料,这在提高系统速度的同时也加大了系统的内存占用,特别是当缓存的资料比较多的时候。其实我们可以使用操作系统中的缓存的概念来解决这个问题,也就是给被缓存的分配一个一定大小的缓存容器,按照一定的算法淘汰不需要继续缓存的对象,这样一方面会因为进行了对象缓存而提高了系统的运行效率,同时由于缓存容器不是无限制扩大,从而也减少了系统的内存占用。现在有很多开源的缓存实现项目,比如ehcache、oscache等,这些项目都

7、实现了FIFO、MRU等常见的缓存算法。(5)避免过深的类层次结构和过深的方法调用。因为这两者都是非常占用内存的(特别是方法调用更是堆栈空间的消耗大户)。(6)变量只有在用到它的时候才定义和实例化。(7)尽量避免使用static变量,类内私有常量可以用final来代替。 1. java是如何管理内存的         Java的内存管理就是对象的分配和释放问题。(两部分)        分配 :内存的分配是由程序完成的,程序员需要通过关键字new

8、 为每个对象申请内存空间 (基本类型除外,所有的对象都在堆 (Heap中分配空间。        释放 :对象的释放是由垃圾回收机制决定和执行的,这样做确实简化了程序员的工作。但同时,它也加重了JVM的工作。这也是Java程序运行速度较慢的原因 之一。因为,GC为了能够正确释放对象,GC必须监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等,GC都需要进行监控。 2.  什么叫java的内存泄露        在Java中,

9、内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是可达的,即在有向图中,存在通路可以与其相连(也就是说仍存在该内存对象的引用);其次,这些对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存。        与C+内存泄露概念的区别:         在C+中,内存泄漏的范围更大一些。有些对象被分配了内存空间,然后却不可达,由于C+中

10、没有GC,这些内存将永远收不回来。在Java中,这些不可达的对象都由GC负责回收,因此程序员不需要考虑这部分的内存泄露。3.    JVM的内存区域组成        java把内存分两种:一种是栈内存,另一种是堆内存        1。在函数中定义的基本类型变量和对象的引用变量都在函数的栈内存中分配;        2。堆内存用来存放由new创建的对象和数

11、组以及对象的实例变量         在函数(代码块)中定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量所分配的内存空间;在堆中分配的内存由java虚拟机的自动垃圾回收器来管理 堆和栈的优缺点         堆的优势是可以动态分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的。缺点就是要在运行时动态分配内存,存取速度较慢;栈的优势是,存取速度比堆要快,仅次于直接位于

12、CPU中的寄存器。另外,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。4.    Java中数据在内存中是如何存储的a    基本数据类型         Java的基本数据类型共有8种,即int, short, long, byte, float, double, boolean, char(注意,并没有string的基本类型。这种类型的定义是通过诸如int a = 3; long b = 255L;的形式来定义的。如in

13、t a = 3;这里的a是一个指向int类型的引用,指向3这个字面值。这些字面值的数据,由于大小可知,生存期可知(这些字面值定义在某个程序块里面,程序块退出后,字段值就消失了,出于追求速度的原因,就存在于栈中。         另外,栈有一个很重要的特殊性,就是存在栈中的数据可以共享。         比如:我们同时定义:        int a = 3; 

14、0;            int b=3;        编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找有没有字面值为3的地址,没找到,就开辟一个存放3这个字面值的地址,然后将a指向3的地址。接着处理int b = 3;在创建完b这个引用变量后,由于在栈中已经有3这个字面值,便将b直接指向3的地址。这样,就出现了a与b同时均指向3的情况。    

15、0;  定义完a与b的值后,再令a = 4;那么,b不会等于4,还是等于3。在编译器内部,遇到时,它就会重新搜索栈中是否有4的字面值,如果没有,重新开辟地址存放4的值;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。b    对象        在Java中,创建一个对象包括对象的声明和实例化两步,下面用一个例题来说明对象的内存模型。  假设有类Rectangle定义如下: Java代码 1. class Rectangle 

16、;  2.    3.         double width,height;   4.    5.         Rectangle(double w,double h   6.            &

17、#160;       width=w;   7.                       height=h;   8.             

18、     9.   class Rectangle double width,height; Rectangle(double w,double h width=w;height=h;       (1声明对象时的内存模型 用Rectangle rect;声明一个对象rect时,将在栈内存为对象的引用变量rect分配内存空间,但Rectangle的值为空,称rect是一个空对象。空对象不能使用,因为它还没有引用任何“实体”。(2对象实例化时的内存模型 当执行rect=new

19、Rectangle(3,5;时,会做两件事:在堆内存中为类的成员变量width,height分配内存,并将其初始化为各数据类型的默认值;接着进行显式初始化(类定义时的初始化值);最后调用构造方法,为成员变量赋值。      返回堆内存中对象的引用(相当于首地址)给引用变量rect,以后就可以通过rect来引用堆内存中的对象了。c    创建多个不同的对象实例        一个类通过使用new运算符可以创建多个不同的对象实例,这些对象实例将在堆中被分配不同

20、的内存空间,改变其中一个对象的状态不会影响其他对象的状态。例如:Java代码 1. Rectangle r1=new Rectangle(3,5;   2. Rectangle r2=new Rectangle(4,6;  Rectangle r1=new Rectangle(3,5;Rectangle r2=new Rectangle(4,6;此时,将在堆内存中分别为两个对象的成员变量width、height分配内存空间,两个对象在堆内存中占据的空间是互不相同的。如果有:Java代码 1. Rectangle&#

21、160;r1=new Rectangle(3,5;   2. Rectangle r2=r1;  Rectangle r1=new Rectangle(3,5;Rectangle r2=r1;则在堆内存中只创建了一个对象实例,在栈内存中创建了两个对象引用,两个对象引用同时指向一个对象实例。 d    包装类         基本型别都有对应的包装类:如int对应Integer类,double对应Double类等,基本

22、类型的定义都是直接在栈中,如果用包装类来创建对象,就和普通对象一样了。例如:int i=0;i直接存储在栈中。  Integer i(i此时是对象) = new Integer(5;这样,i对象数据存储在堆中,i的引用存储在栈中,通过栈中的引用来操作对象。 e    String         String是一个特殊的包装类数据。可以用用以下两种方式创建:         1.   String str = new Stri

23、ng("abc";         2.   String str = "abc";         第一种创建方式,和普通对象的的创建过程一样;         第二种创建方式,Java内部将此语句转化为以下几个步骤:      (1 先定义一个名为str的对String类的对

24、象引用变量:String str;      (2 在栈中查找有没有存放值为“abc”的地址,如果没有,则开辟一个存放字面值为“abc”的地址,接着创建一个新的String类的对象o,并将o的字符串值指向这个地址,而且在栈中这个地址旁边记下这个引用的对象o。如果已经有了值为“abc”的地址,则查找对象o,并返回o的地址。      (3 将str指向对象o的地址。         值得注意的是,一般String类中字

25、符串值都是直接存值的。但像String str = "abc";这种场合下,其字符串值却是保存了一个指向存在栈中数据的引用。         为了更好地说明这个问题,我们可以通过以下的几个代码进行验证。 Java代码 1. String str1=“abc”;   2. String str2=“abc”;   3. System.out.println(s1=s2;/true  String str1=“

26、abc”;String str2=“abc”;System.out.println(s1=s2;/true          注意,这里并不用str1.equals(str2;的方式,因为这将比较两个字符串的值是否相等。=号,根据JDK的说明,只有在两个引用都指向了同一个对象时才返回真值。而我们在这里要看的是,str1与str2是否都指向了同一个对象。  我们再接着看以下的代码。Java代码 1. Stringstr1=new String(“abc”;   2. Strin

27、gstr2=“abc”;   3. System.out.println(str1=str2;/false  Stringstr1=new String(“abc”;Stringstr2=“abc”;System.out.println(str1=str2;/false      创建了两个引用。创建了两个对象。两个引用分别指向不同的两个对象。   以上两段代码说明,只要是用new(来新建对象的,都会在堆中创建,而且其字符串是单独存值的,即使与栈中的数据相同,也不会与栈中的数据共享。 

28、f    数组         当定义一个数组,int x;或int x;时,在栈内存中创建一个数组引用,通过该引用(即数组名)来引用数组。x=new int3;将在堆内存中分配3个保存int型数据的空间,堆内存的首地址放到栈内存中,每个数组元素被初始化为0。 g    静态变量         用static的修饰的变量和方法,实际上是指定了这些变量和方法在内存中的

29、“固定位置”static storage,可以理解为所有实例对象共有的内存空间。static变量有点类似于C中的全局变量的概念;静态表示的是内存的共享,就是它的每一个实例都指向同一个内存地址。把static拿来,就是告诉JVM它是静态的,它的引用(含间接引用)都是指向同一个位置,在那个地方,你把它改了,它就不会变成原样,你把它清理了,它就不会回来了。         那静态变量与方法是在什么时候初始化的呢?对于两种不同的类属性,static属性与instance属性,初始化的时机是不同的。instance属性在创建

30、实例的时候初始化,static属性在类加载,也就是第一次用到这个类的时候初始化,对于后来的实例的创建,不再次进行初始化。         我们常可看到类似以下的例子来说明这个问题:Java代码 1. class Student   2.         static int numberOfStudents=0;   3.    

31、0;    Student(   4.            5.                  numberOfStudents+;   6.         &#

32、160;  7.   class Studentstatic int numberOfStudents=0;Student(numberOfStudents+;         每一次创建一个新的Student实例时,成员numberOfStudents都会不断的递增,并且所有的Student实例都访问同一个 numberOfStudents变量,实际上int numberOfStudents变量在内存中只存储在一个位置上。J2EE程序的性能优化技巧 取决于细节文章分类:Java编程

33、应用J2EE平台开发的系统的性能是系统使用者和开发者都关注的问题,本文从服务器端编程时应注意的几个方面讨论代码对性能的影响,并总结一些解决的建议。关键词:性能,Java,J2EE,EJB,Servlet,JDBC 一、概要Java 2 Platform, Enterprise Edition (J2EE是当前很多商业应用系统使用的开发平台,该技术提供了一个基于组件的方法来设计、开发、装配和部署企业级应用程序。J2EE平台提供了一个多层结构的分布式的应用程序模型,可以更快地开发和发布的新的应用解决方案。 J2EE是一种技术规范,定义了整个标准的应用开发体系结构和一个部署环境,应用开发者

34、开发时只要专注于具体商业逻辑和商业业务规则的实现上,而其他的诸如事务、持久化、安全等系统开发问题可以由应用程序容器或者服务器处理,开发完成后,就可以方便地部署到实现规范的应用服务器中。 作为网络上的商业应用系统,同时访问的人数是很多的,在大量访问的情况下,过多的资源请求和有限的服务器资源(内存、CPU时间、网络带宽等)之间就会出现矛盾,应用系统的性能就显得很重要了,有时正确的代码并不能保证项目的成功,性能往往是最后决定一个项目是否成功关键。本文主要从性能的角度出发,讨论J2EE服务器端的代码性能优化和提升。二、常见的Java 编程 J2EE语言基础是Java,常用的Java代码问题对

35、应用系统的性能影响,下面讨论了一些应该注意方面。 使用StringBuffer代替String 当处理字符串的相加时,常见的写法是:String str1 = "Hello"String str2 = "welcome to world"String str3 = str1 + ", " + str2 +"!"System.out.println(str3;很多人都知道,这样的代码效率是很低的,因为String是用来存储字符串常量的,如果要执行“”的操作,系统会生成一些临时的对象,并对这些对象进行管理,造

36、成不必要的开销。如果字符串有连接的操作,替代的做法是用StringBuffer类的append方法,它的缺省构造函数和append的实现是:public StringBuffer( / 构造函数this(16; / 缺省容量16 public synchronized StringBuffer append(String str if (str = null str = String.valueOf(str; int len =str.length(; int newcount = count + len; if(newcount > value.length expandCapacit

37、y(newcount; / 扩充容量str.getChars(0, len, value, count; count = newcount; return this; 当字符串的大小超过缺省16时,代码实现了容量的扩充,为了避免对象的重新扩展其容量,更好的写法为:StringBuffer buffer = new StringBuffer(30; / 分配指定的大小。buffer.append("hello" buffer.append("," buffer.append("welcometo world!" String str =

38、 buffer.toString(;·生成对象时,分配合理的空间和大小Java中的很多类都有它的默认的空间分配大小,对于一些有大小的对象的初始化,应该预计对象的大小,然后使用进行初始化,上面的例子也说明了这个问题,StringBuffer创建时,我们指定了它的大小。另外的一个例子是Vector,当声明Vector vectnew Vector(时,系统调用:public Vector( / 缺省构造函数this(10; / 容量是 10;缺省分配10个对象大小容量。当执行add方法时,可以看到具体实现为:. public synchronized boolean add(Object

39、 o modCount+;ensureCapacityHelper(elementCount+1;elementDataelementCount+ =o;return true;private void ensureCapacityHelper(int minCapacity int oldCapacity = elementData.length; if (minCapacity > oldCapacity Object oldData = elementData; int newCapacity = (capacityIncrement > 0 ? (oldCapacity +

40、 capacityIncrement : (oldCapacity * 2; if (newCapacity < minCapacity newCapacity = minCapacity; elementData = new ObjectnewCapacity; System.arraycopy(oldData, 0, elementData, 0, elementCount; 我们可以看到,当Vector大小超过原来的大小时,一些代码的目的就是为了做容量的扩充,在预先知道该Vector大小的话,可以指定其大小,避免容量扩充的开销,如知道Vector大小为100时,初始化是就可以象这样

41、。Vector vect . new Vector(100;·优化循环体循环是比较重复运行的地方,如果循环次数很大,循环体内不好的代码对效率的影响就会被放大而变的突出。考虑下面的代码片:. Vector vect = new Vector(1000;.for( inti=0; i . for循环部分改写成:int size = vect.size(;for( int i=0; i>size; i+.如果size=1000,就可以减少1000次size(的系统调用开销,避免了循环体重复调用。再看如下的代码片:. for (int i = 0;i <100000;i+if (

42、i%10 = 9 . / 每十次执行一次改写成也可以提高效率:. for(inti =0,j =10; i<100000; i+,j-if(j = 0. / 每十次执行一次j = 10;所以,当有较大的循环时,应该检查循环内是否有效率不高的地方,寻找更优的方案加以改进。·对象的创建尽量少用new来初始化一个类的实例,当一个对象是用new进行初始化时,其构造函数链的所有构造函数都被调用到,所以new操作符是很消耗系统资源的,new一个对象耗时往往是局部变量赋值耗时的上千倍。同时,当生成对象后,系统还要花时间进行垃圾回收和处理。当new创建对象不可避免时,注意避免多次的使用new初

43、始化一个对象。尽量在使用时再创建该对象。如:NewObject object = new NewObject(;int value;if(i>0 value =object.getValue(;可以修改为:int value;if(i>0 NewObject object = new NewObject(; Value =object.getValue(; 另外,应该尽量重复使用一个对象,而不是声明新的同类对象。一个重用对象的方法是改变对象的值,如可以通过setValue之类的方法改变对象的变量达到重用的目的。·变量的注意事项尽量使用局部变量,调用方法时传递的参数以及在调

44、用中创建的临时变量都保存在栈(Stack) 中,速度较快。其他变量,如静态变量、实例变量等,都在堆(Heap)中创建,速度较慢。尽量使用静态变量,即加修饰符static,如果类中的变量不会随他的实例而变化,就可以定义为静态变量,从而使他所有的实例都共享这个变量。·方法(Method)调用在Java中,一切都是对象,如果有方法(Method)调用,处理器先要检查该方法是属于哪个对象,该对象是否有效,对象属于什么类型,然后选择合适的方法并调用。可以减少方法的调用,同样一个方法: public void CallMethod(int i if( i =0 return;. / 其他处理如果

45、直接调用,int i = 0; . CallMethod(i;就不如写成:int i = 0; . if( i =0 CallMethod(i; 不影响可读性等情况下,可以把几个小的方法合成一个大的方法。另外,在方法前加上final,private关键字有利于编译器的优化。·慎用异常处理 异常是Java的一种错误处理机制,对程序来说是非常有用的,但是异常对性能不利。抛出异常首先要创建一个新的对象,并进行相关的处理,造成系统的开销,所以异常应该用在错误处理的情况,不应该用来控制程序流程,流程尽量用while,if等处理。在不是很影响代码健壮性的前提下,可以把几个try/catch块合成

46、一个。·同步同步主要出现在多线程的情况,为多线程同时运行时提供对象数据安全的机制,多线程是比较复杂话题,应用多线程也是为了获得性能的提升,应该尽可能减少同步。另外,如果需要同步的地方,可以减少同步的代码段,如只同步某个方法或函数,而不是整个代码。·使用Java系统APIJava的API一般都做了性能的考虑,如果完成相同的功能,优先使用API而不是自己写的代码,如数组复制通常的代码如下: int size = 1000;String strArray1 = new Stringsize;String strArray2 = new Stringsize;for(inti=0;

47、i 赋值 strArray1i = (new String("Array: " + i; for(inti=0;i 复制 strArray2i=(new String(Stringai; 如果使用Java提供的API,就可以提高性能:int size = 1000;String strArray1 = new Stringsize;String strArray2 = new Stringsize;for(inti=0;i 赋值 strArray1i = (new String("Array: " + i; System.arraycopy(strArr

48、ay1,0,strArray2,0,size; / 复制 同样的一个规则是,当有大量数据的复制时,应该使用System.arraycopy(。 三、I/O 性能输入/输出(I/O)包括很多方面,我们知道,进行I/O操作是很费系统资源的。程序中应该尽量少用I/O操作。使用时可以注意: . 合理控制输出函数System.out.println(对于大多时候是有用的,特别是系统调试的时候,但也会产生大量的信息出现在控制台和日志上,同时输出时,有序列化和同步的过程,造成了开销。特别是在发行版中,要合理的控制输出,可以在项目开发时,设计好一个Debug的工具类,在该类中可以实现输出开关,输出的级别,根据

49、不同的情况进行不同的输出的控制。·使用缓存读写内存要比读写文件要快很多,应尽可能使用缓冲。尽可能使用带有Buffer的类代替没有Buffer的类,如可以用BufferedReader 代替Reader,用BufferedWriter代替Writer来进行处理I/O操作。同样可以用BufferedInputStream代替InputStream都可以获得性能的提高。四、ServletServlet采用请求响应模式提供Web服务,通过ServletResponse以及ServletRequest这两个对象来输出和接收用户传递的参数,在服务器端处理用户的请求,根据请求访问数据库、访问别的S

50、ervlet方法、调用EJB等等,然后将处理结果返回给客户端。·尽量不使用同步Servlet是多线程的,以处理不同的请求,基于前面同步的分析,如果有太多的同步就失去了多线程的优势了。 ·不用保存太多的信息在HttpSession中很多时候,存储一些对象在HttpSession中是有必要的,可以加快系统的开发,如网上商店系统会把购物车信息保存在该用户的Session中,但当存储大量的信息或是大的对象在会话中是有害的,特别是当系统中用户的访问量很大,对内存的需求就会很高。具体开发时,在这两者之间应作好权衡。·清除Session通常情况,当达到设定的超时时间时,同时有些

51、Session没有了活动,服务器会释放这些没有活动的Session,. 不过这种情况下,特别是多用户并访时,系统内存要维护多个的无效Session。当用户退出时,应该手动释放,回收资源,实现如下:. HttpSession theSession = request.getSession(; / 获取当前Sessionif(theSession != null theSession.invalidate(; / 使该Session失效五、EJB 问题EJB是Java服务器端服务框架的规范,软件厂商根据它来实现EJB服务器。应用程序开发者可以专注于支持应用所需的商业逻辑,而不用担心周围框架的实现问

52、题。EJB规范详细地解释了一些最小但是必须的服务,如事务,安全和名字等。·缓存Home接口EJB库使用Enterprise Bean 的客户端通过它的Home接口创建它的实例。客户端能通过JNDI访问它。服务器通过Lookup方法来获取。JNDI是个远程对象,通过RMI方式调用,对它的访问往往是比较费时的。所以,在设计时可以设计一个类专门用来缓存Home接口,在系统初始化时就获得需要的Home接口并缓存,以后的引用只要引用缓存即可。·封装Entity Bean直接访问Entity Bean是个不好的习惯,用会话Bean封装对实体Bean的访问能够改进事务管理,因为每一个对g

53、et方法的直接调用将产生一个事务,容器将在每一个实体Bean的事务之后执行一个“Load-Store”. 操作。最好在Session Bean中完成Entity Bean的封装,减少容器的事务处理,并在Session Bean中实现一些具体的业务方法。·释放有状态的Session Bean相当于HttpSession,当把一个Session Bean设为Stateful,即有状态的Session Bean 后,应用容器(Container)就可能有“钝化”(Passivate)和活化(Activate)过程,即在主存和二级缓存之间对SessionBean进行存储位置的转移,在这个过程

54、中,存在序列化过程。通常有状态Session Bean的释放是在超时时发生,容器自动的清除该对象,但是如果交给容器管理,一方面可能产生对象钝化,另一方面未超时期间,系统还要 维护一份该对象,所以如果我们确认使用完该StatefulSession Bean后不再需要时,可以显式的将其释放掉,方法是调用:theSesionBean.remove(;六、数据库访问在J2EE开发的应用系统中,数据库访问一般是个必备的环节。数据库用来存储业务数据,供应用程序访问。在Java技术的应用体系中,应用程序是通过JDBC(Java Database Connectivity)实现的接口来访问数据库的,JDBC支

55、持“建立连接、SQL语句查询、处理结果”等基本功能。在应用JDBC接口访问数据库的过程中,只要根据规范来实现,就可以达到要求的功能。但是,有些时候进行数据查询的效率着实让开发人员不如所愿,明明根据规范编写的程序,运行效果却很差,造成整个系统的执行效率不高。·使用速度快的JDBC驱动JDBC API包括两种实现接口形式,一种是纯Java实现的驱动,一种利用ODBC驱动和数据库客户端实现,具体有四种驱动模式并各有不同的应用范围,针对不同的应用开发要选择合适的JDBC驱动,在同一个应用系统中,如果选择不同的JDBC驱动,在效率上会有差别。例如,有一个企业应用系统,不要求支持不同厂商的数据库

56、,这时就可以选择模式4的JDBC驱动,该驱动一般由数据库厂商实现的基于本地协议的驱动,直接调用数据库管理系统使用的协议,减少了模式3中的中间层。·使用JDBC连接池为了提高访问数据库的性能,我们还可以使用的一些规范和特性,JDBC是占用资源的,在使用数据库连接时可以使用连接池Connection Pooling,避免频繁打开、关闭Connection。而我们知道,获取Connection是比较消耗系统资源的。Connection缓冲池是这样工作的:当一个应用程序关闭一个数据库连接时,这个连接并不真正释放而是被循环利用,建立连接是消耗较大的操作,循环利用连接可以显著的提高性能,因为可以

57、减少新连接的建立。一个通过DataSource获取缓冲池获得连接,并连接到一个CustomerDB数据源的代码演示如下:Context ctx = new InitialContext(;DataSource dataSource = (DataSource ctx.lookup("jdbc/CustomerDB"Connection conn = dataSource.getConnection("password","username"·缓存DataSource一个DataSource对象代表一个实际的数据源。这个数据源

58、可以是从关系数据库到表格形式的文件,完全依赖于它是怎样实现的,一个数据源对象注册到JNDI名字服务后,应用程序就可以从JNDI服务器上取得该对象,并使用之和数据源建立连接。通过上面的例子,我们知道DataSource是从连接池获得连接的一种方式,通过JNDI方式获得,是占用资源的。为了避免再次的JNDI调用,可以系统中缓存要使用的DataSource。·关闭所有使用的资源系统一般是并发的系统,在每次申请和使用完资源后,应该释放供别人使用,数据库资源每个模式的含义可以参考SUN JDBC的文档,不同是比较宝贵的,使用完成后应该保证彻底的释放。 请看下面的代码段: Connection

59、conn = null; Statement stmt = null; ResultSet rs = null; try DataSource dataSource = getDataSource(; / 取的DataSource的方法,实现略。conn = datasource.getConnection(; stmt = conn.createStatement(; rs = stmt.executeQuery("SELECT * FROM ." . / 其他处理rs.close(;stmt.close(;conn.close(;catch (SQLException

60、ex . / 错误处理粗看似乎没有什么问题,也有关闭相关如Connection等系统资源的代码,但当出现异常后,关闭资源的代码可能并不被执行,为保证资源的确实已被关闭,应该把资源关闭的代码放到finally块:Connection conn = null; Statement stmt = null; ResultSet rs = null; try DataSource dataSource = getDataSource(; / 取的DataSource的方法,实现略。conn = datasource.getConnection(; stmt = conn.createStatement

61、(; rs = stmt.executeQuery("SELECT * FROM ." . / 其他处理catch (SQLException ex . / 错误处理finally if (rs!=null try rs.close(; / 关闭ResultSet catch (SQLException ex . / 错误处理 if (stmt!=null try stmt.close(; / 关闭Statement catch (SQLException ex . / 错误处理 if (conn!=null try conn.close(; / 关闭Connection

62、catch (SQLException ex . / 错误处理 ·大型数据量处理当我们在读取诸如数据列表、报表等大量数据时,可以发现使用EJB的方法是非常慢的,这时可以使用直接访问数据库的方法,用SQL直接存取数据,从而消除EJB的经常开支(例如远程方法调用、事务管理和数据序列化,对象的构造等)。·缓存经常使用的数据对于构建的业务系统,如果有些数据要经常要从数据库中读取,同时,这些数据又不经常变化,这些数据就可以在系统中缓存起来,使用时直接读取缓存,而不用频繁的访问数据库读取数据。缓存工作可以在系统初始化时一次性读取数据,特别是一些只读的数据,当数据更新时更新数据库内容,同

63、时更新缓存的数据值。一个例子是,在一套企业应用系统中,企业的信息数据(如企业的名称)在多个业务应用模块中使用,这时就可以把这些数据缓存起来,需要时直接读取缓存的企业信息数据。七、总结一般意义上说,参与系统运行的代码都会对性能产生影响,实际应用中应该养成良好的编程规范、编写高质量的代码,当系统性能出现问题时,要找到主要影响性能的瓶颈所在,然后集中精力优化这些代码,能达到事半功倍的效果。J2EE性能的优化包括很多方面的,要达到一个性能优良的系统,除了关注代码之外,还应该根据系统实际的运行情况,从服务器软硬件环境、集群技术、系统构架设计、系统部署环境、数据结构、算法设计等方面综合考虑。 在Java程序中节约内存的10个最佳技巧来源:it前沿路口  作者:it前沿路口   时间:2009-12-11 13:06:55  浏览:70次【IT前沿路口】 垃圾回收(GC),有些东西你可以不担心,但有的时候会很烦人。Java开发新手在编写代码的时候不会考虑程序对内存的使用情况。经常有人会说垃圾回收器会回收任何被使用的内存,但我会说,这是错的。当应用的内存溢出时,开发者就开始咒骂Java,说它的内存管理无效。难道Java是罪魁祸首?或者说由程序消耗的内存是问题所在

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论