转自:http://blog.csdn.net/gaofei_upc/archive/2009/12/17/5026951.aspx
高 飞
摘 要:由Java的内存管理机制谈起,分析了Java程序中的内存泄漏问题的原因,列举了典型的内存泄漏问题并给出了一些解决方法,最后讨论了如何找出程序中内存泄漏的问题。
关键字:Java内存泄漏;Java垃圾回收器;弱引用
附 件:无
大多数程序员都知道,使用Java编程语言的一大好处就是,不必再担心内存的分配和释放问题。您只须创建对象,当应用程序不再需要这些对象时,Java 会通过一种称为“垃圾回收”的机制将这些对象的内存释放掉。他们认为Java不存在内存泄漏问题,或者认为即使有内存泄漏也不是程序的责任,而是垃圾回收器(GC)或Java虚拟机(JVM)的问题。但事实真的是这样吗?Java真的已经解决了困扰其他编程语言的内存泄露问题了吗?
一、Java的内存管理机制
在进一步讨论之前,我们先了解一下Java的内存管理机制。Java的内存管理就是对象的分配和释放问题。在Java中,程序员需要通过关键字new为每个对象申请内存空间 (基本类型除外),所有的对象都在堆 (Heap)中分配空间。在Java中,内存的分配是由程序完成的,而内存的释放是则是由垃圾回收器决定和执行的,这种收支两条线的方法确实简化了程序员的工作。但同时,它也加重了JVM的负担,这也是Java程序运行速度较慢的原因之一。因为,垃圾回收器为了能够正确回收对象,必须监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等。监视对象状态是为了准确、及时地释放对象,而释放对象的基本原则就是该对象是否仍被引用。
垃圾收集器的工作是发现应用程序不再需要的对象,并在这些对象不再被访问或引用时将它们删除。垃圾收集器从根节点(在 Java 应用程序的整个生存周期内始终存在的那些类)开始,遍历所有仍被引用的节点,进行垃圾回收。任何对象只要不再被引用,就符合垃圾回收的条件。垃圾回收器回收这些对象后,它们所占用的内存资源也就被返回给了Java虚拟机。
Java使用有向图的方式进行内存管理,可以消除循环引用的问题,例如有三个对象,相互引用,只要它们和根线程不可达,那么垃圾回收器也是可以回收它们的。这种方式的优点是管理内存的精度很高,但是效率较低。另外一种常用的内存管理技术是使用计数器,例如COM模型采用计数器方式管理构件,它与有向图相比,精度较低(很难处理循环引用的问题),但执行效率却很高。
为了更好理解地垃圾回收器的工作原理,我们可以将对象考虑为有向图的顶点,将引用关系考虑为图的有向边,有向边从引用对象指向被引对象。每个线程可以作为一个图的起始顶点,例如大多程序从main线程开始执行,那么该图就是以main线程为顶点的一个有向图。在这个有向图中,根顶点可达的对象都是有效对象,如果某个对象不可达,那么垃圾回收器会认为这个对象不再被引用,可以被回收。对于程序的每一个时刻,我们都有一个有向图表示JVM的内存分配情况。
二、什么是Java中的内存泄露
在C++ 程序中,内存泄漏是指应用程序为某些对象被分配了内存空间,然后却因为某些原因不可达,以至于被这些对象使用的内存无法被释放并返还给操作系统,这些内存将永远收不回来。
令人欣慰的是,这种内存泄露问题在Java程序中并不存在。在Java中,对象使用的内存都由垃圾回收器负责回收的,而Java虚拟机并不存在任何被证实的内存泄漏问题。实践证明,垃圾收集器一般能够精确地判断哪些对象可被收集,回收它们占用的内存空间并返还给Java 虚拟机。
对于Java来说,内存泄漏是指在程序中存在一些实际上并不需要的对象引用。这些对象有下面两个特点,首先,这些对象是可达的,即在有向图中,存在通路可以与其相连;其次,这些对象是无用的,即程序以后不会再使用这些对象。一个典型的例子是向一个集合中加入一些对象以便以后使用它们,但是却没有在使用完后从集合中删除对这些对象的引用。因为集合可以无限制地扩大,并且从来不会变小,所以当向集合中加入了太多的对象(或者是有很多的对象被集合中的元素所引用)时,就会因为堆空间被填满而导致内存耗尽。垃圾收集器并不会把这些您认为已经用完的对象当作垃圾进行回收,因为对于垃圾收集器来说,应用程序仍然可以通过这个集合在任何时候访问这些对象。
通过以上分析,可以知道在Java中也有内存泄漏,但范围比C++要小一些。因为Java从语言上保证,任何对象都是可达的,都由垃圾回收器进行内存的回收管理。
随着越来越多的服务器程序、嵌入式系统及游戏平台采用Java技术,出现了较多内存有限、需要长期运行Java应用。内存泄露问题也就变得十分关键,即使每次少量内存泄漏,长期运行之后,系统也有面临内存溢出的危险。
三、典型的内存泄漏问题及解决方法
我们知道了在Java中确实会存在内存泄漏,那么就让我们看一看几种典型的泄漏,并试图找出他们的解决方法。
3.1 全局集合
在大型应用程序中存在各种各样的全局数据储存库是很普遍的,比如一个Session Table。在这些情况下,必须注意管理储存库的大小。必须使用某种机制从储存库中移除不再需要的数据。
通常有很多不同的解决形式,其中最常用的一种是周期运行的清除作业。这个作业会验证仓库中的数据然后清除一切不需要的数据。
另一种管理储存库的方法是使用反向链接(Referrer)计数。然后集合负责统计集合中每个元素反向链接的数目,当反向链接数目为零时,该元素就可以从集合中移除了。
3.2 缓存
缓存一种用来快速查找已经执行过的操作结果的数据结构。因此,如果一个操作执行需要比较多的资源并会多次被使用,通常做法是把常用的输入数据的操作结果进行缓存,以便在下次调用该操作时使用缓存中的数据。缓存通常都是以动态方式实现的,如果缓存设置不正确而大量使用缓存的话,则会出现内存溢出的后果,因此需要将所使用的内存容量与检索数据的速度加以平衡。
常用的解决途径是使用软引用或弱引用类将对象放入缓存。这个方法可以保证当虚拟机用完内存或者需要更多堆的时候,可以释放这些对象的引用。
3.3 类装载器
Java类装载器的使用为内存泄漏提供了许多可乘之机。一般来说类装载器都具有复杂结构,因为类装载器不仅仅是只与“常规”的对象引用有关,同时也和对象内部的引用有关。比如数据变量,方法和各种类。这意味着只要存在对数据变量,方法,各种类和对象的引用,那么类装载器将驻留在Java虚拟机中。既然类装载器可以同很多的类关联,同时也可能和静态数据变量关联,那么相当多的内存就可能发生泄漏。
3.4 物理连接
一些物理连接,比如数据库连接和网络连接,除非其显式的关闭了连接,否则是不会自动被GC 回收的。Java 数据库连接一般用DataSource.getConnection()来创建,当不再使用时必须用Close()方法来释放。对于ResultSet 和Statement 对象可以不进行显式回收,但Connection 一定要显式回收,,因为这些连接是独立于Java虚拟机的,在任何时候都无法自动回收,而Connection一旦回收,ResultSet 和Statement 对象就会立即变为NULL。但是如果使用连接池,情况就不一样了,除了要显式地关闭连接,还必须显式地关闭ResultSet和Statement 对象(关闭其中一个,另外一个也会关闭),否则就会造成大量的Statement 对象无法释放,从而引起内存泄漏。
3.5 内部模块和外部模块等的引用
内部类的引用是比较容易遗忘的一种,而且一旦没释放可能导致一系列的后继类对象没有释放。对于程序员而言,自己的程序很清楚,如果发现内存泄漏,自己对这些对象的引用可以很快定位并解决。但是在大型应用软件的开发中,整个系统并非一个人实现,个人担当的可能只是系统的某一机能或某机能的一个模块。所以程序员要小心外部模块不经意的引用,例如程序员A 负责A 模块,调用了B模块的一个方法如:public void registerMsg(Object b); 这种调用就要非常小心了,传入了一个对象,很可能模块B就保持了对该对象的引用,这时候就需要注意模块B是否需要提供相应的去除引用的操作。
四、如何找出内存泄漏
查找内存泄漏一般有两种方法:一是安排有经验的编程人员对代码进行走查和分析,找出内存泄漏发生的位置;二是使用专门的内存泄漏测试工具进行测试。
第一种方法,在代码走查工作中,可以安排对系统业务和开发语言较熟悉的开发人员对应用的代码进行了交叉走查,尽量找出代码中存在的数据库连接声明和结果集未关闭、代码冗余等问题代码。
第二种方法就是使用专门的内存泄漏工具进行测试。市场上已有专业检查Java内存泄漏的工具,它们的基本工作原理大同小异,都是通过监测Java程序运行时,所有对象的申请、释放等动作,将内存管理的所有信息进行统计、分析、可视化。开发人员将根据这些信息判断程序是否有内存泄漏问题。常用的工具有Optimizeit Profiler,JProbe Profiler,JinSight以及Rational公司的Purify等。
参考文献:
[1]赋空变量和垃圾收集http://www.javaspecialists.co.za/archive/newsletter.do?issue=060.
[2]Java 2引用类使用指南http://www.ibm.com/developerworks/cn/java/j-refs/index.html
[3]用弱引用堵住内存泄漏 http://www.ibm.com/developerworks/cn/java/j-jtp11225/
[4]Does Java Technology Have Memory Leaks? http://www.klgroup.com/javaone
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/gaofei_upc/archive/2009/12/17/5026951.aspx
分享到:
相关推荐
8. **内存泄漏**:虽然Java有垃圾收集器,但不正确的编程习惯可能导致内存泄漏,例如静态集合类中存储大量无用对象,或者线程池未关闭等。找出并修复内存泄漏是提升程序性能的重要环节。 9. **Java内存模型(JMM)*...
在Java编程中,内存泄漏是一个严重的问题,它会导致程序性能下降,甚至可能导致应用程序崩溃。内存泄漏是指程序在申请内存后,无法释放已不再使用的内存空间,随着时间推移,系统可用内存逐渐减少,从而影响系统运行...
Java内存泄漏是软件开发中一个常见的问题,它不仅会影响应用程序的性能,还可能导致系统崩溃。通过深入了解Java的内存管理机制,并借助于专业的工具如OptimizeIt,可以有效地检测和解决内存泄漏问题。此外,开发者还...
3. **分析内存泄漏**:MAT提供多种视图来帮助定位问题,其中饼状图是最直观的一种。通过查看" Dominator Tree "视图,可以看到内存消耗最大的对象和它们之间的引用关系。此外,"Leak Suspects"报告会自动分析可能的...
4. **定位代码问题**:根据引用链回溯到代码,找出导致内存泄漏的代码片段,可能是因为资源未关闭、静态集合类中添加了大量对象等原因。 5. **修复并验证**:修改代码,消除内存泄露,并重新运行程序验证是否解决...
Java内存泄漏是一个严重的问题,它会导致程序性能下降,甚至可能导致应用程序崩溃。为了有效地诊断和解决这类问题,开发者需要借助特定的分析工具。本篇将详细探讨Java内存泄漏及其相关的分析工具。 内存泄漏是指...
Java程序中的内存泄露是一个复杂而重要的主题,许多人误以为由于其自动垃圾回收(Garbage Collection, GC)机制的存在,Java应用程序就不会出现内存问题。然而,事实并非如此简单。垃圾回收虽然能够有效地管理内存,...
在Java开发过程中,经常会遇到内存泄漏的问题,尤其是在长时间运行的应用程序中更为常见。本文将详细介绍如何解决Java内存泄漏问题,帮助开发者更好地理解和应对这一挑战。 #### 二、Java内存模型与内存区域 Java...
### JAVA程序内存泄漏综述 #### 一、Java内存泄漏基本概念 在程序开发中,内存管理是一项重要的任务。不同的编程语言采用了不同的内存管理机制。本文重点讨论Java内存泄漏问题,并将其与C/C++的内存泄漏进行对比...
Java内存泄露是Java开发中常见的一种问题,发生内存泄露可能会导致Java应用程序崩溃或性能下降。在Java中,内存泄露的原因非常多样,例如,静态变量、循环引用、数据库连接池、Session溢出等等。为了检测和解决Java...
### Java之内存泄露 ...总之,虽然Java的自动垃圾回收机制大大减轻了开发者在内存管理方面的负担,但在实际开发过程中仍需注意内存泄露问题。通过采取上述措施,可以有效避免内存泄露,确保程序的稳定性和性能。
在Java系统中,内存泄漏是一个严重的问题,它可能导致系统性能下降,甚至系统崩溃。这篇研究主要探讨了如何检测和分析Java应用中的内存泄漏问题。以下是对这个主题的详细阐述: 一、内存泄漏的理解 内存泄漏是指...
要有效避免Java中的内存泄漏问题,需要采取以下措施: 1. **使用工具进行检测**:例如VisualVM、JProfiler等工具可以帮助开发者检测内存泄漏的具体位置。 2. **定期审查代码**:确保所有的资源都能得到适当的管理和...
Java编程语言在处理大型应用...总的来说,结合JMAP和MAT,我们可以有效地定位和解决Java应用程序中的内存泄漏问题。确保定期监控和分析内存使用情况,可以预防和早期发现潜在的内存问题,从而保持应用的高效稳定运行。
Java内存泄漏问题是一个重要的主题,尤其对于大型的J2EE应用程序而言,理解并避免内存泄漏至关重要。虽然Java的垃圾收集机制能自动管理内存,但并不意味着程序员可以完全忽视内存管理。以下是一些关于Java内存泄漏的...
总的来说,JAVA内存泄漏分析工具,尤其是MAT,是Java开发者必备的诊断利器,它能够帮助我们高效地排查和解决内存相关问题,确保应用程序的稳定性和性能。通过熟练掌握这类工具的使用,开发者可以更好地优化代码,...
在Java编程中,内存泄漏是一个严重的问题,它可能导致系统性能下降,甚至系统崩溃。这篇博客“JAVA内存泄漏问题处理方法经验总结”分享了作者在处理此类问题时的一些实用技巧和经验,结合源码分析和工具使用,对于...
在Java中,由于具备垃圾回收机制(GC),理论上开发者不必担心内存泄露问题。然而,在实际应用中,仍然存在一些特殊情况会导致内存泄露的发生。 #### 二、内存泄露的原因分析 1. **未及时关闭资源**:例如文件句柄、...
Java内存泄露检测是Java开发中一个关键的议题,因为它直接影响到程序的稳定性和资源效率。内存泄露是指程序中已分配的内存无法被正确地释放,从而导致系统资源的浪费和可能导致程序性能下降甚至崩溃。 首先,理解...