`
jefferent
  • 浏览: 80247 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

JAVA内存泄露

阅读更多

一、什么是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;i<100; i++)
{
Object o=new Object();
v.add(o);
o=null;
}
//此时,所有的Object对象都没有被释放,因为变量v引用这些对象。

 

二. 几种典型的内存泄漏

    我们知道了在Java中确实会存在内存泄漏,那么就让我们看一看几种典型的泄漏,并找出他们发生的原因和解决方法。

1、全局集合
    在大型应用程序中存在各种各样的全局数据仓库是很普遍的,比如一个JNDI-tree或者一个session table。在这些情况下,必须注意管理储存库的大小。必须有某种机制从储存库中移除不再需要的数据。

通常有很多不同的解决形式,其中最常用的是一种周期运行的清除作业。这个作业会验证仓库中的数据然后清除一切不需要的数据。

另一种管理储存库的方法是使用反向链接(referrer)计数。然后集合负责统计集合中每个入口的反向链接的数目。这要求反向链接告诉集合何时会退出入口。当反向链接数目为零时,该元素就可以从集合中移除了。   
2、缓存
    缓存一种用来快速查找已经执行过的操作结果的数据结构。因此,如果一个操作执行需要比较多的资源并会多次被使用,通常做法是把常用的输入数据的操作结果进行缓存,以便在下次调用该操作时使用缓存的数据。缓存通常都是以动态方式实现的,如果缓存设置不正确而大量使用缓存的话则会出现内存溢出的后果,因此需要将所使用的内存容量与检索数据的速度加以平衡。

    常用的解决途径是使用java.lang.ref.SoftReference类坚持将对象放入缓存。这个方法可以保证当虚拟机用完内存或者需要更多堆的时候,可以释放这些对象的引用。

3、类装载器
    Java类装载器的使用为内存泄漏提供了许多可乘之机。一般来说类装载器都具有复杂结构,因为类装载器不仅仅是只与"常规"对象引用有关,同时也和对象内部的引用有关。比如数据变量,方法和各种类。这意味着只要存在对数据变量,方法,各种类和对象的类装载器,那么类装载器将驻留在JVM中。既然类装载器可以同很多的类关联,同时也可以和静态数据变量关联,那么相当多的内存就可能发生泄漏。

 

三. 如何检测和处理内存泄漏

    如何查找引起内存泄漏的原因一般有两个步骤:第一是安排有经验的编程人员对代码进行走查和分析,找出内存泄漏发生的位置;第二是使用专门的内存泄漏测试工具进行测试。

    第一个步骤在代码走查的工作中,可以安排对系统业务和开发语言工具比较熟悉的开发人员对应用的代码进行了交叉走查,尽量找出代码中存在的数据库连接声明和结果集未关闭、代码冗余等故障代码。

    第二个步骤就是检测Java的内存泄漏。在这里我们通常使用一些工具来检查Java程序的内存泄漏问题。市场上已有几种专业检查Java内存泄漏的工具,它们的基本工作原理大同小异,都是通过监测Java程序运行时,所有对象的申请、释放等动作,将内存管理的所有信息进行统计、分析、可视化。开发人员将根据这些信息判断程序是否有内存泄漏问题。这些工具包括Optimizeit Profiler,JProbe Profiler,JinSight , Rational 公司的Purify等。

1、检测内存泄漏的存在

    这里我们将简单介绍我们在使用Optimizeit检查的过程。通常在知道发生内存泄漏之后,第一步是要弄清楚泄漏了什么数据和哪个类的对象引起了泄漏。

    一般说来,一个正常的系统在其运行稳定后其内存的占用量是基本稳定的,不应该是无限制的增长的。同样,对任何一个类的对象的使用个数也有一个相对稳定的上限,不应该是持续增长的。根据这样的基本假设,我们持续地观察系统运行时使用的内存的大小和各实例的个数,如果内存的大小持续地增长,则说明系统存在内存泄漏,如果特定类的实例对象个数随时间而增长(就是所谓的“增长率”),则说明这个类的实例可能存在泄漏情况。

    另一方面通常发生内存泄漏的第一个迹象是:在应用程序中出现了OutOfMemoryError。在这种情况下,需要使用一些开销较低的工具来监控和查找内存泄漏。虽然OutOfMemoryError也有可能应用程序确实正在使用这么多的内存;对于这种情况则可以增加JVM可用的堆的数量,或者对应用程序进行某种更改,使它使用较少的内存。
    但是,在许多情况下,OutOfMemoryError都是内存泄漏的信号。一种查明方法是不间断地监控GC的活动,确定内存使用量是否随着时间增加。如果确实如此,就可能发生了内存泄漏。

2、处理内存泄漏的方法

    一旦知道确实发生了内存泄漏,就需要更专业的工具来查明为什么会发生泄漏。JVM自己是不会告诉您的。这些专业工具从JVM获得内存系统信息的方法基本上有两种:JVMTI和字节码技术(byte code instrumentation)。Java虚拟机工具接口(Java Virtual Machine Tools Interface,JVMTI)及其前身Java虚拟机监视程序接口(Java Virtual Machine Profiling Interface,JVMPI)是外部工具与JVM通信并从JVM收集信息的标准化接口。字节码技术是指使用探测器处理字节码以获得工具所需的信息的技术。

    Optimizeit是Borland公司的产品,主要用于协助对软件系统进行代码优化和故障诊断,其中的Optimizeit Profiler主要用于内存泄漏的分析。Profiler的堆视图就是用来观察系统运行使用的内存大小和各个类的实例分配的个数的。

首先,Profiler会进行趋势分析,找出是哪个类的对象在泄漏。
    接下来,看看有哪些其他的类与泄漏的类的对象相关联。
    最后,进一步研究单个对象,看看它们是如何互相关联的。借助于Profiler工具,应用程序中的代码可以在分配时进行动态添加,以创建堆栈跟踪。总而言之, Java虽然有自动回收管理内存的功能,但内存泄漏也是不容忽视,它往往是破坏系统稳定性的重要因素。

 

四. 内存泄漏与内存溢出的区别

1、内存泄漏是指分配出去的内存无法回收了
2、内存溢出是指程序要求的内存,超出了系统所能分配的范围,从而发生溢出。
    为了便于理解,我们不妨打个比方。缓冲区溢出好比是将十磅的糖放进一个只能装五磅的容器里。一旦该容器放满了,余下的部分就溢出在柜台和地板上,弄得一团糟。由于计算机程序的编写者写了一些编码,但是这些编码没有对目的区域或缓冲区——五磅的容器——做适当的检查,看它们是否够大,能否完全装入新的内容——十磅的糖,结果可能造成缓冲区溢出的产生。如果打算被放进新地方的数据不适合,溢得到处都是,该数据也会制造很多麻烦。但是,如果缓冲区仅仅溢出,这只是一个问题。到此时为止,它还没有破坏性。当糖溢出时,柜台被盖住。可以把糖擦掉或用吸尘器吸走,还柜台本来面貌。与之相对的是,当缓冲区溢出时,过剩的信息覆盖的是计算机内存中以前的内容。除非这些被覆盖的内容被保存或能够恢复,否则就会永远丢失。
3、一个是收不回,一个是要的太多给不了,系统负担不起。

 

分享到:
评论
3 楼 sinly 2011-12-06  
好像是深入java虚拟机上的内容
2 楼 sinly 2011-12-06  
这个是你自己写的还是引用书上的?
1 楼 sinly 2011-12-06  

相关推荐

Global site tag (gtag.js) - Google Analytics