guoln 发表于 2012-1-6 10:51:40

Java内存泄露的问题


Java内存泄露的问题一 问题的提出Java的一个重要优点就是经过渣滓收集器(Garbage Collection,GC)自动管理内存的回收,程序员不需要经过调用函数来释放内存。因此,很多程序员认为Java不存在内存走漏问题,或许认为即使有内存走漏也不是程序的责任,而是GC或JVM的问题。其实,这种想法是不正确的,因为Java也存在内存泄露,但它的表现与C++不同。随着越来越多的服务器程序采用Java技术,例如JSP,Servlet, EJB等,服务器程序往往临时运行。另外,在很多嵌入式系统中,内存的总量十分有限。内存泄露问题也就变得十分关键,即使每次运行大批走漏,临时运行之后,系统也是面临崩溃的风险。二 Java是如何管理内存为了判别Java中能否有内存泄露,我们首先必需了解Java是如何管理内存的。Java的内存管理就是对象的分配和释放问题。在Java中,程序员需要经过关键字new为每个对象申请内存空间 (基本类型除外),一切的对象都在堆 (Heap)中分配空间。另外,对象的释放是由GC决议和执行的。在Java中,内存的分配是由程序完成的,而内存的释放是有GC完成的,这种收支两条线的方法确实简化了程序员的工作。但同时,它也减轻了JVM的工作。这也是Java程序运行速度较慢的缘由之一。因为,GC为了能够正确释放对象,GC必需监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等,GC都需要进行监控。监视对象状态是为了更加准确地、及时地释放对象,而释放对象的根本原则就是该对象不再被引用。为了更好了解GC的工作原理,我们可以将对象考虑为有向图的顶点,将引用关系考虑为图的有向边,有向边从引用者指向被引对象。另外,每个线程对象可以作为一个图的起始顶点,例如大多程序从main进程末尾执行,那么该图就是以main进程顶点末尾的一棵根树。在这个有向图中,根顶点可达的对象都是有效对象,GC将不回收这些对象。假设某个对象 (连通子图)与这个根顶点不可达(注意,该图为有向图),那么我们认为这个(这些)对象不再被引用,可以被GC回收。以下,我们举一个例子说明如何用有向图表示内存管理。对于程序的每一个时刻,我们都有一个有向图表示JVM的内存分配情况。以下右图,就是右边程序运行到第6行的示意图。Java使用有向图的方式进行内存管理,可以消弭引用循环的问题,例如有三个对象,相互引用,只需它们和根进程不可达的,那么GC也是可以回收它们的。这种方式的优点是管理内存的精度很高,但是效率较低。另外一种常用的内存管理技术是使用计数器,例如COM模型采用计数器方式管理构件,它与有向图相比,精度行低(很难处理循环引用的问题),但执行效率很高。三 什么是Java中的内存泄露下面,我们就可以描画什么是内存走漏。在Java中,内存走漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是可达的,即在有向图中,存在通路可以与其相连;其次,这些对象是无用的,即程序以后不会再使用这些对象。假设对象满足这两个条件,这些对象就可以判定为Java中的内存走漏,这些对象不会被GC所回收,但是它却占用内存。在C++中,内存走漏的范围更大一些。有些对象被分配了内存空间,然后却不可达,由于C++中没有GC,这些内存将永远收不回来。在Java中,这些不可达的对象都由GC担任回收,因此程序员不需要考虑这部分的内存泄露。经过火析,我们得知,对于C++,程序员需要自己管理边和顶点,而对于Java程序员只需要管理边就可以了(不需要管理顶点的释放)。经过这种方式,Java提高了编程的效率。因此,经过以上分析,我们知道在Java中也有内存走漏,但范围比C++要小一些。因为Java从言语上保证,任何对象都是可达的,一切的不可达对象都由GC管理。对于程序员来说,GC基本是通明的,不可见的。虽然,我们只有几个函数可以拜访GC,例如运行GC的函数System.gc(),但是根据Java言语规范定义, 该函数不保证JVM的渣滓收集器一定会执行。因为,不同的JVM实现者能够使用不同的算法管理GC。通常,GC的线程的优先级别较低。JVM调用GC的策略也有很多种,有的是内存使用到达一定水平时,GC才末尾工作,也有定时执行的,有的是平缓执行GC,有的是中断式执行GC。但通常来说,我们不需要关怀这些。除非在一些特定的场合,GC的执行影呼应用程序的性能,例如对于基于Web的实时系统,歌瑞尔如网络游戏等,用户不希望GC突然中断应用程序执行而进行渣滓回收,那么我们需要调整GC的参数,让GC能够经过平缓的方式释放内存,例如将渣滓回收分解为一系列的小步骤执行,Sun提供的HotSpot JVM就支持这一特性。下面给出了一个复杂的内存泄露的例子。在这个例子中,我们循环申请Object对象,并将所申请的对象放入一个Vector中,假设我们仅仅释放引用本身,那么Vector依然引用该对象,所以这个对象对GC来说是不可回收的。因此,假设对象参与到Vector后,还必需从Vector中删除,最复杂的方法就是将Vector对象设置为null。Vector v=new Vector(10);for (int i=1;i100; i++){Object o=new Object();v.add(o);o=null;}//此时,一切的Object对象都没有被释放,因为变量v引用这些对象。



页: [1]
查看完整版本: Java内存泄露的问题