- 浏览: 296178 次
- 性别:
- 来自: 深圳
文章分类
- 全部博客 (133)
- boss好文 (10)
- 数据模型 (2)
- together (1)
- oracle (10)
- 健康生活 (4)
- js好东东 (8)
- 工作流 (1)
- 常见java问题 (7)
- BOSS开发随想 (0)
- rose相关 (1)
- 股市看图 (4)
- java基础 (12)
- jbpm (1)
- 集群(负载均衡) (4)
- spring教程 (2)
- maven入门 (5)
- 项目管理 (14)
- 常用软件 (3)
- mysql (4)
- j2ee性能调优 (2)
- jfreechart相关 (1)
- 需求工具 (2)
- maven基础讲解 (3)
- AXURE下载 (2)
- db2 (2)
- svn (1)
- 日常操作技巧 (3)
- SOA (16)
- jetty (2)
- jetspeed (0)
- camel (0)
- 安卓开发 (4)
- ESB (4)
- 物流 (2)
- 软件需求的3个层次 (1)
- WMS (1)
- eclipse (1)
- 安卓源代码 (2)
- jar (0)
最新评论
-
seeYourEye:
prt1979 写道怎么在点“open project”后,选 ...
PL/SQL Developer插件之SVN -
prt1979:
怎么在点“open project”后,选择文件夹后一直弹出“ ...
PL/SQL Developer插件之SVN -
houlianxue:
LateCHI 写道东西不错。可以正常打开目录。但是进行svn ...
PL/SQL Developer插件之SVN -
LateCHI:
东西不错。可以正常打开目录。但是进行svn操作的时候提示 un ...
PL/SQL Developer插件之SVN -
w.tany:
很多地方少个# 号
<s:property>的用法
抽象
尽管java虚拟机和垃圾回收机制管理着大部分的内存事务,但是在java软件中还是可能存在内存泄漏的情况。的确,在大型工程中,内存泄漏是一个普遍问题。避免内存泄漏的第一步,就是要了解他们发生的原因。这篇文章就是要介绍一些常见的缺陷,然后提供一些非常好的实践例子来指导你写出没有内存泄漏的代码。一旦你的程序存在内存泄漏,要查明代码中引起泄漏的原因是很困难的。同时这篇文章也要介绍一个新的工具来查找内存泄漏,然后指明发生的根本原因。这个工具容易上手,可以让你找到产品级系统中的内存泄漏。
垃圾回收(GC)的角色
虽然垃圾回收关心着大部分的问题,包括内存管理,使得程序员的任务显得更加轻松,但是程序员还是可能犯些错误导致内存泄漏问题。GC(垃圾回收)通过递归对所有从"根"对象(堆栈中的对象,静态数据成员,JNI句柄等等)继承下来的引用进行工作,然后标记所有可以访问的活着的对象。而这些对象变成了程序唯一能够操纵的对象,其他的对象都被释放了。因为GC使得程序不能够访问那些被释放的对象,所以这样做是安全的。
内存管理可以说是自动的,但是这并没有让程序员脱离内存管理问题。比方说,对于内存的分配(还有释放)总是存在一定的开销,尽管这些开销对程序员来说是暗含的。一个程序如果创建了很多对象,那么它就要比完成相同任务而创建了较少对象的程序执行的速度慢(其他提供的内容都相同)。
导致内存泄漏主要的原因是,先前申请了内存空间而忘记了释放。如果程序中存在对无用对象的引用,那么这些对象就会驻留内存,消耗内存,因为无法让垃圾回收器验证这些对象是否不再需要。正如我们前面看到的,如果存在对象的引用,这个对象就被定义为"活着的",同时不会被释放。要确定对象所占内存将被回收,程序员就要务必确认该对象不再会被使用。典型的做法就是把对象数据成员设为null或者从集合中移除该对象。注意,当局部变量不需要时,不需明显的设为null,因为一个方法执行完毕时,这些引用会自动被清理。
从更高一个层次看,这就是所有存在内存管的语言对内存泄漏所考虑的事情,剩余的对象引用将不再会被使用。
典型泄漏
既然我们知道了在java中确实会存在内存泄漏,那么就让我们看一些典型的泄漏,并找出他们发生的原因。
全局集合
在大型应用程序中存在各种各样的全局数据仓库是很普遍的,比如一个JNDI-tree或者一个session table。在这些情况下,注意力就被放在了管理数据仓库的大小上。当然是有一些适当的机制可以将仓库中的无用数据移除。
可以有很多不同的解决形式,其中最常用的是一种周期运行的清除作业。这个作业会验证仓库中的数据然后清除一切不需要的数据。
另一个办法是使用引用计算。集合用来对了解每个集合入口关联器(referrer)的数目负责。这要求关联器通知集合什么时候完成进入。当关联器的数目为零时,就可以移除集合中的相关元素。
高速缓存
高速缓存是一种用来快速查找已经执行过的操作结果的数据结构。因此,如果一个操作执行很慢的话,你可以先把普通输入的数据放入高速缓存,然后过些时间再调用高速缓存中的数据。
高速缓存多少还有一点动态实现的意思,当数据操作完毕,又被送入高速缓存。一个典型的算法如下所示:
1.检查结果是否在高速缓存中,存在则返回结果;
2.如果结果不在,那么计算结果;
3.将结果放入高速缓存,以备将来的操作调用。
这个算法的问题(或者说潜在的内存泄漏)在最后一步。如果操作伴随着一个不同的,输入非常大的数字,那么存入高速缓存的也是一个非常大的结果。那么这个方法就不是能够胜任的了。
为了避免这种潜在的致命错误设计,程序就必须确定高速缓存在他所使用的内存中有一个上界。因此,更好的算法是:
1.检查结果是否在高速缓存中,存在则返回结果;
2.如果结果不在,那么计算结果;
3.如果高速缓存所占空间过大,移除缓存中旧的结果;
4.将结果放入高速缓存,以备将来的操作调用。
通过不断的从缓存中移除旧的结果,我们可以假设,将来,最新输入的数据可能被重用的几率要远远大于旧的结果。这通常是一个不错的设想。
这个新的算法会确保高速缓存的容量在预先确定的范围内。精确的范围是很难计算的,因为缓存中的对象存在引用时将继续有效。正确的划分高速缓存的大小是一个复杂的任务,你必须权衡可使用内存大小和数据快速存取之间的矛盾。
另一个解决这个问题的途径是使用java.lang.ref.SoftReference类坚持将对象放入高速缓存。这个方法可以保证当虚拟机用完内存或者需要更多堆的时候,可以释放这些对象的引用。
类装载器
Java类装载器创建就存在很多导致内存泄漏的漏洞。由于类装载器的复杂结构,使得很难得到内存泄漏的透视图。这些困难不仅仅是由于类装载器只与"普通的"对象引用有关,同时也和对象内部的引用有关,比如数据变量,方法和各种类。这意味着只要存在对数据变量,方法,各种类和对象的类装载器,那么类装载器将驻留在JVM中。既然类装载器可以同很多的类关联,同时也可以和静态数据变量关联,那么相当多的内存就可能发生泄漏。
定位内存泄漏
常常地,程序内存泄漏的最初迹象发生在出错之后,得到一个OutOfMemoryError在你的程序中。这种典型地情况发生在产品环境中,而在那里,你希望内存泄漏尽可能的少,调试的可能性也达到最小。也许你的测试环境和产品的系统环境不尽相同,导致泄露的只会在产品中揭示。这种情况下,你需要一个低内务操作工具来监听和寻找内存泄漏。同时,你还需要把这个工具同你的系统联系起来,而不需要重新启动他或者机械化你的代码。也许更重要的是,当你做分析的时候,你需要能够同工具分离而使得系统不会受到干扰。
一个OutOfMemoryError常常是内存泄漏的一个标志,有可能应用程序的确用了太多的内存;这个时候,你既不能增加JVM的堆的数量,也不能改变你的程序而使得他减少内存使用。但是,在大多数情况下,一个OutOfMemoryError是内存泄漏的标志。一个解决办法就是继续监听GC的活动,看看随时间的流逝,内存使用量是否会增加,如果有,程序中一定存在内存泄漏。
详细输出
有很多办法来监听垃圾回收器的活动。也许运用最广泛的就是以:-Xverbose:gc选项运行JVM,然后观察输出结果一段时间。
[memory] 10.109-10.235: GC 65536K->16788K (65536K), 126.000 ms
箭头后的值(在这个例子中 16788K)是垃圾回收后堆的使用量。
控制台
观察这些无尽的GC详细统计输出是一件非常单调乏味的事情。好在有一些工具来代替我们做这些事情。The JRockit Management Console可以用图形的方式输出堆的使用量。通过观察图像,我们可以很方便的观察堆的使用量是否伴随时间增长。
Figure 1. The JRockit Management Console
管理控制台甚至可以配置成在堆使用量出现问题(或者其他的事件发生)时向你发送邮件。这个显然使得监控内存泄漏更加容易。
内存泄漏探测工具
有很多专门的内存泄漏探测工具。其中The JRockit Memory Leak Detector可以供来观察内存泄漏也可以针对性地找到泄漏的原因。这个强大的工具被紧密地集成在JRockit JVM中,可以提供最低可能的内存事务也可以轻松的访问虚拟机的堆。
专门工具的优势
一旦你知道程序中存在内存泄漏,你需要更专业的工具来查明为什么这里会有泄漏。而JVM是不可能告诉你的。现在有很多工具可以利用了。这些工具本质上主要通过两种方法来得到JVM的存储系统信息的:JVMTI和字节码使用仪器。Java虚拟机工具接口(JVMTI)和他的原有形式JVMPI(压型接口)都是标准接口,作为外部工具同JVM进行通信,搜集JVM的信息。字节码使用仪器则是引用通过探针获得工具所需的字节信息的预处理技术。
通过这些技术来侦测内存泄漏存在两个缺点,而这使得他们在产品级环境中的运用不够理想。首先,根据两者对内存的使用量和内存事务性能的降级是不可以忽略的。从JVM获得的堆的使用量信息需要在工具中导出,收集和处理。这意味着要分配内存。按照JVM的性能导出信息是需要开销的,垃圾回收器在搜集信息的时候是运行的非常缓慢的。另一个缺点就是,这些工具所需要的信息是关系到JVM的。让工具在JVM开始运行的时候和它关联,而在分析的时候,分离工具而保持JVM运行,这显然是不可能的。
既然JRockit Memory Leak Detector是被集成到JVM中的,那么以上两种缺点就不再适用。首先,大部分的处理和分析都是在JVM中完成的,所以就不再需要传送或重建任何数据。处理也可以在垃圾回收器的背上,他的意思是提高速度。再有,内存泄漏侦测器可以同一个运行的JVM关联和分离,只要JVM在开始的时候伴随着 -Xmanagement选项(这个允许监听和管理JVM通过远程JMX接口)。当工具分离以后,工具不会遗留任何东西在JVM中;JVM就可以全速运行代码就好像工具关联之前一样。
1 引言
Java的一个重要优点就是通过垃圾收集器GC (Garbage Collection)自动管理内存的回收,程序员不需要通过调用函数来释放内存。因此,很多程序员认为Java 不存在内存泄漏问题,或者认为即使有内存泄漏也不是程序的责任,而是GC 或JVM的问题。其实,这种想法是不正确的,因为Java 也存在内存泄漏,但它的表现与C++不同。如果正在开发的Java 代码要全天24 小时在服务器上运行,则内存漏洞在此处的影响就比在配置实用程序中的影响要大得多,即使最小的漏洞也会导致JVM耗尽全部可用内存。另外,在很多嵌入式系统中,内存的总量非常有限。在相反的情况下,即便程序的生存期较短,如果存在分配大量临时对象(或者若干吞噬大量内存的对象)的任何Java 代码,而且当不再需要这些对象时也没有取消对它们的引用,则仍然可能达到内存极限。
2 Java 内存回收机制
Java 的内存管理就是对象的分配和释放问题。分配内存的方式多种多样,取决于该种语言的语法结构。但不论是哪一种语言的内存分配方式,最后都要返回所分配的内存块的起始地址,即返回一个指针到内存块的首地址。在Java 中所有对象都是在堆(Heap)中分配的,对象的创建通常都是采用new或者是反射的方式,但对象释放却有直接的手段,所以对象的回收都是由Java虚拟机通过垃圾收集器去完成的。这种收支两条线的方法确实简化了程序员的工作,但同时也加重了JVM的工作,这也是Java 程序运行速度较慢的原因之一。因为,GC 为了能够正确释放对象,GC 必须监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等,GC 都需要进行监控。监视对象状态是为了更加准确地、及时地释放对象,而释放对象的根本原则就是该对象不再被引用。Java 使用有向图的方式进行内存管理,可以消除引用循环的问题,例如有三个对象,相互引用,只要它们和根进程不可达,那么GC 也是可以回收它们的。在Java 语言中,判断一块内存空间是否符合垃圾收集器收集标准的标准只有两个:一个是给对象赋予了空值null,以下再没有调用过,另一个是给对象赋予了新值,即重新分配了内存空间。
3 Java 中的内存泄漏
3.1 Java 中内存泄漏与C++的区别
在Java 中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是可达的,即在有向图中,存在通路可以与其相连;其次,这些对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对象就可以判定为Java 中的内存泄漏,这些对象不会被GC 所回收,然而它却占用内存。在C++中,内存泄漏的范围更大一些。有些对象被分配了内存空间,然后却不可达,由于C++中没有GC,这些内存将永远收不回来。在Java 中,这些不可达的对象都由GC 负责回收,因此程序员不需要考虑这部分的内存泄漏。通过分析,可以得知,对于C++,程序员需要自己管理边和顶点,而对于Java 程序员只需要管理边就可以了(不需要管理顶点的释放)。通过这种方式,Java 提高了编程的效率。
3.2 内存泄漏示例
3.2.1 示例1
在这个例子中,循环申请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 引用这些对象。实际上无用,而还被引用的对象,GC 就无能为力了(事实上GC 认为它还有用),这一点是导致内存泄漏最重要的原因。
(1)如果要释放对象,就必须使其的引用记数为0,只有那些不再被引用的对象才能被释放,这个原理很简单,但是很重要,是导致内存泄漏的基本原因,也是解决内存泄漏方法的宗旨;
(2)程序员无须管理对象空间具体的分配和释放过程,但必须要关注被释放对象的引用记数是否为0;
(3)一个对象可能被其他对象引用的过程的几种:
a.直接赋值,如上例中的A.a = E;
b.通过参数传递,例如public void addObject(Object E);
c.其它一些情况如系统调用等。
3.3 容易引起内存泄漏的几大原因
3.3.1 静态****类
像HashMap、Vector 等静态****类的使用最容易引起内存泄漏,因为这些静态变量的生命周期与应用程序一致,如示例1,如果该Vector 是静态的,那么它将一直存在,而其中所有的Object对象也不能被释放,因为它们也将一直被该Vector 引用着。
3.3.2 监听器
在java 编程中,我们都需要和监听器打交道,通常一个应用当中会用到很多监听器,我们会调用一个控件的诸如addXXXListener()等方法来增加监听器,但往往在释放对象的时候却没有记住去删除这些监听器,从而增加了内存泄漏的机会。
3.3.3 物理连接
一些物理连接,比如数据库连接和网络连接,除非其显式的关闭了连接,否则是不会自动被GC 回收的。Java 数据库连接一般用DataSource.getConnection()来创建,当不再使用时必须用Close()方法来释放,因为这些连接是独立于JVM的。对于Resultset 和Statement 对象可以不进行显式回收,但Connection 一定要显式回收,因为Connection 在任何时候都无法自动回收,而Connection一旦回收,Resultset 和Statement 对象就会立即为NULL。但是如果使用连接池,情况就不一样了,除了要显式地关闭连接,还必须显式地关闭Resultset Statement 对象(关闭其中一个,另外一个也会关闭),否则就会造成大量的Statement 对象无法释放,从而引起内存泄漏。
3.3.4 内部类和外部模块等的引用
内部类的引用是比较容易遗忘的一种,而且一旦没释放可能导致一系列的后继类对象没有释放。对于程序员而言,自己的程序很清楚,如果发现内存泄漏,自己对这些对象的引用可以很快定位并解决,但是现在的应用软件并非一个人实现,模块化的思想在现代软件中非常明显,所以程序员要小心外部模块不经意的引用,例如程序员A 负责A 模块,调用了B 模块的一个方法如:
public void registerMsg(Object b);
这种调用就要非常小心了,传入了一个对象,很可能模块B就保持了对该对象的引用,这时候就需要注意模块B是否提供相应的操作去除引用。
4 预防和检测内存漏洞
在了解了引起内存泄漏的一些原因后,应该尽可能地避免和发现内存泄漏。
(1)好的编码习惯。
最基本的建议就是尽早释放无用对象的引用,大多数程序员在使用临时变量的时候,都是让引用变量在退出活动域后,自动设置为null。在使用这种方式时候,必须特别注意一些复杂的对象图,例如数组、列、树、图等,这些对象之间有相互引用关系较为复杂。对于这类对象,GC 回收它们一般效率较低。如果程序允许,尽早将不用的引用对象赋为null。另外建议几点:
在确认一个对象无用后,将其所有引用显式的置为null;
当类从Jpanel 或Jdialog 或其它容器类继承的时候,删除该对象之前不妨调用它的removeall()方法;在设一个引用变量为null 值之前,应注意该引用变量指向的对象是否被监听,若有,要首先除去监听器,然后才可以赋空值;当对象是一个Thread 的时候,删除该对象之前不妨调用它的interrupt()方法;内存检测过程中不仅要关注自己编写的类对象,同时也要关注一些基本类型的对象,例如:int[]、String、char[]等等;如果有数据库连接,使用try...finally 结构,在finally 中关闭Statement 对象和连接。
(2)好的测试工具。
在开发中不能完全避免内存泄漏,关键要在发现有内存泄漏的时候能用好的测试工具迅速定位问题的所在。市场上已有几种专业检查Java 内存泄漏的工具,它们的基本工作原理大同小异,都是通过监测Java 程序运行时,所有对象的申请、释放等动作,将内存管理的所有信息进行统计、分析、可视化。开发人员将根据这些信息判断程序是否有内存泄漏问题。这些工具包括Optimizeit Profiler、JProbe Profiler、JinSight、Rational 公司的Purify 等。
文章出处:http://www.diybl.com/course/3_program/java/javashl/20071129/89296.html
解决Java内存泄漏JAVA中文站社区门户.v0F6J1X2y$}
"[4u&? mO Java内存泄漏是每个Java程序员都会遇到的问题,程序在本地运行一切正常,可是布署到远端就会出现内存无限制的增长,最后系统瘫痪,那么如何最快最好的检测程序的稳定性,防止系统崩盘,作者用自已的亲身经历与各位分享解决这些问题的办法.
$BU,T6\4@*Q4b@ W(O.k
+J8` v6Q&b {w l} 作为Internet最流行的编程语言之一,Java现正非常流行.我们的网络应用程序就主要采用Java语言开发,大体上分为客户端、服务器和数据库三个层次.在进入测试过程中,我们发现有一个程序模块系统内存和CPU资源消耗急剧增加,持续增长到出现java.lang.OutOfMemoryError为止.经过分析Java内存泄漏是破坏系统的主要因素.这里与大家分享我们在开发过程中遇到的Java内存泄漏的检测和处理解决过程.JAVA中文站社区门户 k^N8ki
JAVA中文站社区门户 u(s\'[~S"Y [l d
一. Java是如何管理内存JAVA中文站社区门户{3Um xc&WK
z9o#Q `3y"J XuWt 为了判断Java中是否有内存泄露,我们首先必须了解Java是如何管理内存的.Java的内存管理就是对象的分配和释放问题.在Java中,内存的分配是由程序完成的,而内存的释放是由垃圾收集器(Garbage Collection,GC)完成的,程序员不需要通过调用函数来释放内存,但它只能回收无用并且不再被其它对象引用的那些对象所占用的空间.
Y m,NVa@"C
Yn1B*dG(i2U U Java的内存垃圾回收机制是从程序的主要运行对象开始检查引用链,当遍历一遍后发现没有被引用的孤立对象就作为垃圾回收.GC为了能够正确释放对象,必须监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等,GC都需要进行监控.监视对象状态是为了更加准确地、及时地释放对象,而释放对象的根本原则就是该对象不再被引用.JAVA中文站社区门户q Ig q(]A:M
V^{b8` T 在Java中,这些无用的对象都由GC负责回收,因此程序员不需要考虑这部分的内存泄露.虽然,我们有几个函数可以访问GC,例如运行GC的函数System.gc(),但是根据Java语言规范定义,该函数不保证JVM的垃圾收集器一定会执行.因为不同的JVM实现者可能使用不同的算法管理GC.通常GC的线程的优先级别较低.JVM调用GC的策略也有很多种,有的是内存使用到达一定程度时,GC才开始工作,也有定时执行的,有的是平缓执行GC,有的是中断式执行GC.但通常来说,我们不需要关心这些.JAVA中文站社区门户~|bRs Z&`\d:xQ:k$q
JAVA中文站社区门户0g+~ x'a;T9J;Qe
二. 什么是Java中的内存泄露JAVA中文站社区门户%L'B,t-{ j4[$@J)j
JAVA中文站社区门户9H4q }+m.Ti*}H
导致内存泄漏主要的原因是,先前申请了内存空间而忘记了释放.如果程序中存在对无用对象的引用,那么这些对象就会驻留内存,消耗内存,因为无法让垃圾回收器GC验证这些对象是否不再需要.如果存在对象的引用,这个对象就被定义为"有效的活动",同时不会被释放.要确定对象所占内存将被回收,我们就要务必确认该对象不再会被使用.典型的做法就是把对象数据成员设为null或者从集合中移除该对象.但当局部变量不需要时,不需明显的设为null,因为一个方法执行完毕时,这些引用会自动被清理.JAVA中文站社区门户*N\&H9OmS
pnkfQ\)o 在Java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是有被引用的,即在有向树形图中,存在树枝通路可以与其相连;其次,这些对象是无用的,即程序以后不会再使用这些对象.如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存.JAVA中文站社区门户t/O(kc6eV{R
,X7G6d f0v2Mz V 这里引用一个常看到的例子,在下面的代码中,循环申请Object对象,并将所申请的对象放入一个Vector中,如果仅仅释放对象本身,但因为Vector仍然引用该对象,所以这个对象对GC来说是不可回收的.因此,如果对象加入到Vector后,还必须从Vector中删除,最简单的方法就是将Vector对象设置为null.实际上这些对象已经是无用的,但还被引用,GC就无能为力了(事实上GC认为它还有用),这一点是导致内存泄漏最重要的原因. 再引用另一个例子来说明Java的内存泄漏.假设有一个日志类Logger,其提供一个静态的log(String msg),任何其它类都可以调用Logger.Log(message)来将message的内容记录到系统的日志文件中.JAVA中文站社区门户f#FH-w b+IN
JAVA中文站社区门户x*mL@Orr
Logger类有一个类型为HashMap的静态变量temp,每次在执行log(message)的时候,都首先将message的值写入temp中(以当前线程+当前时间为键),在退出之前再从temp中将以当前线程和当前时间为键的条目删除.注意,这里当前时间是不断变化的,所以log在退出之前执行删除条目的操作并不能删除执行之初写入的条目.这样,任何一个作为参数传给log的字符串最终由于被Logger的静态变量temp引用,而无法得到回收,这种对象保持就是我们所说的Java内存泄漏. 总的来说,内存管理中的内存泄漏产生的主要原因:保留下来却永远不再使用的对象引用.
一些在网上搜刮的内容,自己也没有什么好的办法。
1.泄漏的分类, 以发生的方式来分类,内存泄漏可以分为4类:
(1). 常发性内存泄漏。发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。
(2). 偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。
(3). 一次性内存泄漏。发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏。比如,在类的构造函数中分配内存,在析构函数中却没有释放该内存,所以内存泄漏只会发生一次。
(4). 隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。
从用户使用程序的角度来看,内存泄漏本身不会产生什么危害,作为一般的用户,根本感觉不到内存泄漏的存在。真正有危害的是内存泄漏的堆积,这会最终消耗尽系统所有的内存。从这个角度来说,一次性内存泄漏并没有什么危害,因为它不会堆积,而隐式内存泄漏危害性则非常大,因为较之于常发性和偶发性内存泄漏它更难被检测到。
2.内存泄漏的表现
内存泄漏或者是说,资源耗尽后,系统会表现出什么现象哪?
cpu资源耗尽:估计是机器没有反应了,键盘,鼠标,以及网络等等。这个在windows上经常看见,特别是中了毒。
进程id耗尽:没法创建新的进程了,串口或者telnet都没法创建了。
硬盘耗尽: 机器要死了,交换内存没法用,日志也没法用了,死是很正常的。
内存泄漏或者内存耗尽:新的连接无法创建,free的内存比较少。发生内存泄漏的程序很多,但是要想产生一定的后果,就需要这个进程是无限循环的,是个服务进程。当然,内核也是无限循环的,所以,如果内核发生了内存泄漏,情况就更加不妙。内存泄漏是一种很难定位和跟踪的错误,目前还没看到有什么好用的工具(当然,用户空间有一些工具,有静态分析的,也会动态分析的,但是找内核的内存泄漏,没有好的开源工具)
内存泄漏和对象的引用计数有很大的关系,再加上c/c 都没有自动的垃圾回收机制,如果没有手动释放内存,问题就会出现。如果要避免这个问题,还是要从代码上入手,良好的编码习惯和规范,是避免错误的不二法门。
3.内存泄漏的检测
内存泄漏检测工具介绍: http://bbs.51testing.com/thread-87138-1-1.html,只是列出了各个工具,具体实践没有。
另外网上还有很多针对java和c++等的内存泄漏的文章
Java内存泄漏:http://www.blogjava.net/galaxyp/archive/2006/04/28/43724.html
VC6.0内存泄漏检测:http://leoman95.spaces.live.com/blog/cns!87f442ce8fa434b1!131.entry
LR中:如果Process\Private Bytes计数器和Process\Working Set计数器的值持续升高,同时Memory\Available bytes计数器的值却持续降低的话,说明很有可能是存在内存泄漏。
尽管java虚拟机和垃圾回收机制管理着大部分的内存事务,但是在java软件中还是可能存在内存泄漏的情况。的确,在大型工程中,内存泄漏是一个普遍问题。避免内存泄漏的第一步,就是要了解他们发生的原因。这篇文章就是要介绍一些常见的缺陷,然后提供一些非常好的实践例子来指导你写出没有内存泄漏的代码。一旦你的程序存在内存泄漏,要查明代码中引起泄漏的原因是很困难的。同时这篇文章也要介绍一个新的工具来查找内存泄漏,然后指明发生的根本原因。这个工具容易上手,可以让你找到产品级系统中的内存泄漏。
垃圾回收(GC)的角色
虽然垃圾回收关心着大部分的问题,包括内存管理,使得程序员的任务显得更加轻松,但是程序员还是可能犯些错误导致内存泄漏问题。GC(垃圾回收)通过递归对所有从"根"对象(堆栈中的对象,静态数据成员,JNI句柄等等)继承下来的引用进行工作,然后标记所有可以访问的活着的对象。而这些对象变成了程序唯一能够操纵的对象,其他的对象都被释放了。因为GC使得程序不能够访问那些被释放的对象,所以这样做是安全的。
内存管理可以说是自动的,但是这并没有让程序员脱离内存管理问题。比方说,对于内存的分配(还有释放)总是存在一定的开销,尽管这些开销对程序员来说是暗含的。一个程序如果创建了很多对象,那么它就要比完成相同任务而创建了较少对象的程序执行的速度慢(其他提供的内容都相同)。
导致内存泄漏主要的原因是,先前申请了内存空间而忘记了释放。如果程序中存在对无用对象的引用,那么这些对象就会驻留内存,消耗内存,因为无法让垃圾回收器验证这些对象是否不再需要。正如我们前面看到的,如果存在对象的引用,这个对象就被定义为"活着的",同时不会被释放。要确定对象所占内存将被回收,程序员就要务必确认该对象不再会被使用。典型的做法就是把对象数据成员设为null或者从集合中移除该对象。注意,当局部变量不需要时,不需明显的设为null,因为一个方法执行完毕时,这些引用会自动被清理。
从更高一个层次看,这就是所有存在内存管的语言对内存泄漏所考虑的事情,剩余的对象引用将不再会被使用。
典型泄漏
既然我们知道了在java中确实会存在内存泄漏,那么就让我们看一些典型的泄漏,并找出他们发生的原因。
全局集合
在大型应用程序中存在各种各样的全局数据仓库是很普遍的,比如一个JNDI-tree或者一个session table。在这些情况下,注意力就被放在了管理数据仓库的大小上。当然是有一些适当的机制可以将仓库中的无用数据移除。
可以有很多不同的解决形式,其中最常用的是一种周期运行的清除作业。这个作业会验证仓库中的数据然后清除一切不需要的数据。
另一个办法是使用引用计算。集合用来对了解每个集合入口关联器(referrer)的数目负责。这要求关联器通知集合什么时候完成进入。当关联器的数目为零时,就可以移除集合中的相关元素。
高速缓存
高速缓存是一种用来快速查找已经执行过的操作结果的数据结构。因此,如果一个操作执行很慢的话,你可以先把普通输入的数据放入高速缓存,然后过些时间再调用高速缓存中的数据。
高速缓存多少还有一点动态实现的意思,当数据操作完毕,又被送入高速缓存。一个典型的算法如下所示:
1.检查结果是否在高速缓存中,存在则返回结果;
2.如果结果不在,那么计算结果;
3.将结果放入高速缓存,以备将来的操作调用。
这个算法的问题(或者说潜在的内存泄漏)在最后一步。如果操作伴随着一个不同的,输入非常大的数字,那么存入高速缓存的也是一个非常大的结果。那么这个方法就不是能够胜任的了。
为了避免这种潜在的致命错误设计,程序就必须确定高速缓存在他所使用的内存中有一个上界。因此,更好的算法是:
1.检查结果是否在高速缓存中,存在则返回结果;
2.如果结果不在,那么计算结果;
3.如果高速缓存所占空间过大,移除缓存中旧的结果;
4.将结果放入高速缓存,以备将来的操作调用。
通过不断的从缓存中移除旧的结果,我们可以假设,将来,最新输入的数据可能被重用的几率要远远大于旧的结果。这通常是一个不错的设想。
这个新的算法会确保高速缓存的容量在预先确定的范围内。精确的范围是很难计算的,因为缓存中的对象存在引用时将继续有效。正确的划分高速缓存的大小是一个复杂的任务,你必须权衡可使用内存大小和数据快速存取之间的矛盾。
另一个解决这个问题的途径是使用java.lang.ref.SoftReference类坚持将对象放入高速缓存。这个方法可以保证当虚拟机用完内存或者需要更多堆的时候,可以释放这些对象的引用。
类装载器
Java类装载器创建就存在很多导致内存泄漏的漏洞。由于类装载器的复杂结构,使得很难得到内存泄漏的透视图。这些困难不仅仅是由于类装载器只与"普通的"对象引用有关,同时也和对象内部的引用有关,比如数据变量,方法和各种类。这意味着只要存在对数据变量,方法,各种类和对象的类装载器,那么类装载器将驻留在JVM中。既然类装载器可以同很多的类关联,同时也可以和静态数据变量关联,那么相当多的内存就可能发生泄漏。
定位内存泄漏
常常地,程序内存泄漏的最初迹象发生在出错之后,得到一个OutOfMemoryError在你的程序中。这种典型地情况发生在产品环境中,而在那里,你希望内存泄漏尽可能的少,调试的可能性也达到最小。也许你的测试环境和产品的系统环境不尽相同,导致泄露的只会在产品中揭示。这种情况下,你需要一个低内务操作工具来监听和寻找内存泄漏。同时,你还需要把这个工具同你的系统联系起来,而不需要重新启动他或者机械化你的代码。也许更重要的是,当你做分析的时候,你需要能够同工具分离而使得系统不会受到干扰。
一个OutOfMemoryError常常是内存泄漏的一个标志,有可能应用程序的确用了太多的内存;这个时候,你既不能增加JVM的堆的数量,也不能改变你的程序而使得他减少内存使用。但是,在大多数情况下,一个OutOfMemoryError是内存泄漏的标志。一个解决办法就是继续监听GC的活动,看看随时间的流逝,内存使用量是否会增加,如果有,程序中一定存在内存泄漏。
详细输出
有很多办法来监听垃圾回收器的活动。也许运用最广泛的就是以:-Xverbose:gc选项运行JVM,然后观察输出结果一段时间。
[memory] 10.109-10.235: GC 65536K->16788K (65536K), 126.000 ms
箭头后的值(在这个例子中 16788K)是垃圾回收后堆的使用量。
控制台
观察这些无尽的GC详细统计输出是一件非常单调乏味的事情。好在有一些工具来代替我们做这些事情。The JRockit Management Console可以用图形的方式输出堆的使用量。通过观察图像,我们可以很方便的观察堆的使用量是否伴随时间增长。
Figure 1. The JRockit Management Console
管理控制台甚至可以配置成在堆使用量出现问题(或者其他的事件发生)时向你发送邮件。这个显然使得监控内存泄漏更加容易。
内存泄漏探测工具
有很多专门的内存泄漏探测工具。其中The JRockit Memory Leak Detector可以供来观察内存泄漏也可以针对性地找到泄漏的原因。这个强大的工具被紧密地集成在JRockit JVM中,可以提供最低可能的内存事务也可以轻松的访问虚拟机的堆。
专门工具的优势
一旦你知道程序中存在内存泄漏,你需要更专业的工具来查明为什么这里会有泄漏。而JVM是不可能告诉你的。现在有很多工具可以利用了。这些工具本质上主要通过两种方法来得到JVM的存储系统信息的:JVMTI和字节码使用仪器。Java虚拟机工具接口(JVMTI)和他的原有形式JVMPI(压型接口)都是标准接口,作为外部工具同JVM进行通信,搜集JVM的信息。字节码使用仪器则是引用通过探针获得工具所需的字节信息的预处理技术。
通过这些技术来侦测内存泄漏存在两个缺点,而这使得他们在产品级环境中的运用不够理想。首先,根据两者对内存的使用量和内存事务性能的降级是不可以忽略的。从JVM获得的堆的使用量信息需要在工具中导出,收集和处理。这意味着要分配内存。按照JVM的性能导出信息是需要开销的,垃圾回收器在搜集信息的时候是运行的非常缓慢的。另一个缺点就是,这些工具所需要的信息是关系到JVM的。让工具在JVM开始运行的时候和它关联,而在分析的时候,分离工具而保持JVM运行,这显然是不可能的。
既然JRockit Memory Leak Detector是被集成到JVM中的,那么以上两种缺点就不再适用。首先,大部分的处理和分析都是在JVM中完成的,所以就不再需要传送或重建任何数据。处理也可以在垃圾回收器的背上,他的意思是提高速度。再有,内存泄漏侦测器可以同一个运行的JVM关联和分离,只要JVM在开始的时候伴随着 -Xmanagement选项(这个允许监听和管理JVM通过远程JMX接口)。当工具分离以后,工具不会遗留任何东西在JVM中;JVM就可以全速运行代码就好像工具关联之前一样。
1 引言
Java的一个重要优点就是通过垃圾收集器GC (Garbage Collection)自动管理内存的回收,程序员不需要通过调用函数来释放内存。因此,很多程序员认为Java 不存在内存泄漏问题,或者认为即使有内存泄漏也不是程序的责任,而是GC 或JVM的问题。其实,这种想法是不正确的,因为Java 也存在内存泄漏,但它的表现与C++不同。如果正在开发的Java 代码要全天24 小时在服务器上运行,则内存漏洞在此处的影响就比在配置实用程序中的影响要大得多,即使最小的漏洞也会导致JVM耗尽全部可用内存。另外,在很多嵌入式系统中,内存的总量非常有限。在相反的情况下,即便程序的生存期较短,如果存在分配大量临时对象(或者若干吞噬大量内存的对象)的任何Java 代码,而且当不再需要这些对象时也没有取消对它们的引用,则仍然可能达到内存极限。
2 Java 内存回收机制
Java 的内存管理就是对象的分配和释放问题。分配内存的方式多种多样,取决于该种语言的语法结构。但不论是哪一种语言的内存分配方式,最后都要返回所分配的内存块的起始地址,即返回一个指针到内存块的首地址。在Java 中所有对象都是在堆(Heap)中分配的,对象的创建通常都是采用new或者是反射的方式,但对象释放却有直接的手段,所以对象的回收都是由Java虚拟机通过垃圾收集器去完成的。这种收支两条线的方法确实简化了程序员的工作,但同时也加重了JVM的工作,这也是Java 程序运行速度较慢的原因之一。因为,GC 为了能够正确释放对象,GC 必须监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等,GC 都需要进行监控。监视对象状态是为了更加准确地、及时地释放对象,而释放对象的根本原则就是该对象不再被引用。Java 使用有向图的方式进行内存管理,可以消除引用循环的问题,例如有三个对象,相互引用,只要它们和根进程不可达,那么GC 也是可以回收它们的。在Java 语言中,判断一块内存空间是否符合垃圾收集器收集标准的标准只有两个:一个是给对象赋予了空值null,以下再没有调用过,另一个是给对象赋予了新值,即重新分配了内存空间。
3 Java 中的内存泄漏
3.1 Java 中内存泄漏与C++的区别
在Java 中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是可达的,即在有向图中,存在通路可以与其相连;其次,这些对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对象就可以判定为Java 中的内存泄漏,这些对象不会被GC 所回收,然而它却占用内存。在C++中,内存泄漏的范围更大一些。有些对象被分配了内存空间,然后却不可达,由于C++中没有GC,这些内存将永远收不回来。在Java 中,这些不可达的对象都由GC 负责回收,因此程序员不需要考虑这部分的内存泄漏。通过分析,可以得知,对于C++,程序员需要自己管理边和顶点,而对于Java 程序员只需要管理边就可以了(不需要管理顶点的释放)。通过这种方式,Java 提高了编程的效率。
3.2 内存泄漏示例
3.2.1 示例1
在这个例子中,循环申请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 引用这些对象。实际上无用,而还被引用的对象,GC 就无能为力了(事实上GC 认为它还有用),这一点是导致内存泄漏最重要的原因。
(1)如果要释放对象,就必须使其的引用记数为0,只有那些不再被引用的对象才能被释放,这个原理很简单,但是很重要,是导致内存泄漏的基本原因,也是解决内存泄漏方法的宗旨;
(2)程序员无须管理对象空间具体的分配和释放过程,但必须要关注被释放对象的引用记数是否为0;
(3)一个对象可能被其他对象引用的过程的几种:
a.直接赋值,如上例中的A.a = E;
b.通过参数传递,例如public void addObject(Object E);
c.其它一些情况如系统调用等。
3.3 容易引起内存泄漏的几大原因
3.3.1 静态****类
像HashMap、Vector 等静态****类的使用最容易引起内存泄漏,因为这些静态变量的生命周期与应用程序一致,如示例1,如果该Vector 是静态的,那么它将一直存在,而其中所有的Object对象也不能被释放,因为它们也将一直被该Vector 引用着。
3.3.2 监听器
在java 编程中,我们都需要和监听器打交道,通常一个应用当中会用到很多监听器,我们会调用一个控件的诸如addXXXListener()等方法来增加监听器,但往往在释放对象的时候却没有记住去删除这些监听器,从而增加了内存泄漏的机会。
3.3.3 物理连接
一些物理连接,比如数据库连接和网络连接,除非其显式的关闭了连接,否则是不会自动被GC 回收的。Java 数据库连接一般用DataSource.getConnection()来创建,当不再使用时必须用Close()方法来释放,因为这些连接是独立于JVM的。对于Resultset 和Statement 对象可以不进行显式回收,但Connection 一定要显式回收,因为Connection 在任何时候都无法自动回收,而Connection一旦回收,Resultset 和Statement 对象就会立即为NULL。但是如果使用连接池,情况就不一样了,除了要显式地关闭连接,还必须显式地关闭Resultset Statement 对象(关闭其中一个,另外一个也会关闭),否则就会造成大量的Statement 对象无法释放,从而引起内存泄漏。
3.3.4 内部类和外部模块等的引用
内部类的引用是比较容易遗忘的一种,而且一旦没释放可能导致一系列的后继类对象没有释放。对于程序员而言,自己的程序很清楚,如果发现内存泄漏,自己对这些对象的引用可以很快定位并解决,但是现在的应用软件并非一个人实现,模块化的思想在现代软件中非常明显,所以程序员要小心外部模块不经意的引用,例如程序员A 负责A 模块,调用了B 模块的一个方法如:
public void registerMsg(Object b);
这种调用就要非常小心了,传入了一个对象,很可能模块B就保持了对该对象的引用,这时候就需要注意模块B是否提供相应的操作去除引用。
4 预防和检测内存漏洞
在了解了引起内存泄漏的一些原因后,应该尽可能地避免和发现内存泄漏。
(1)好的编码习惯。
最基本的建议就是尽早释放无用对象的引用,大多数程序员在使用临时变量的时候,都是让引用变量在退出活动域后,自动设置为null。在使用这种方式时候,必须特别注意一些复杂的对象图,例如数组、列、树、图等,这些对象之间有相互引用关系较为复杂。对于这类对象,GC 回收它们一般效率较低。如果程序允许,尽早将不用的引用对象赋为null。另外建议几点:
在确认一个对象无用后,将其所有引用显式的置为null;
当类从Jpanel 或Jdialog 或其它容器类继承的时候,删除该对象之前不妨调用它的removeall()方法;在设一个引用变量为null 值之前,应注意该引用变量指向的对象是否被监听,若有,要首先除去监听器,然后才可以赋空值;当对象是一个Thread 的时候,删除该对象之前不妨调用它的interrupt()方法;内存检测过程中不仅要关注自己编写的类对象,同时也要关注一些基本类型的对象,例如:int[]、String、char[]等等;如果有数据库连接,使用try...finally 结构,在finally 中关闭Statement 对象和连接。
(2)好的测试工具。
在开发中不能完全避免内存泄漏,关键要在发现有内存泄漏的时候能用好的测试工具迅速定位问题的所在。市场上已有几种专业检查Java 内存泄漏的工具,它们的基本工作原理大同小异,都是通过监测Java 程序运行时,所有对象的申请、释放等动作,将内存管理的所有信息进行统计、分析、可视化。开发人员将根据这些信息判断程序是否有内存泄漏问题。这些工具包括Optimizeit Profiler、JProbe Profiler、JinSight、Rational 公司的Purify 等。
文章出处:http://www.diybl.com/course/3_program/java/javashl/20071129/89296.html
解决Java内存泄漏JAVA中文站社区门户.v0F6J1X2y$}
"[4u&? mO Java内存泄漏是每个Java程序员都会遇到的问题,程序在本地运行一切正常,可是布署到远端就会出现内存无限制的增长,最后系统瘫痪,那么如何最快最好的检测程序的稳定性,防止系统崩盘,作者用自已的亲身经历与各位分享解决这些问题的办法.
$BU,T6\4@*Q4b@ W(O.k
+J8` v6Q&b {w l} 作为Internet最流行的编程语言之一,Java现正非常流行.我们的网络应用程序就主要采用Java语言开发,大体上分为客户端、服务器和数据库三个层次.在进入测试过程中,我们发现有一个程序模块系统内存和CPU资源消耗急剧增加,持续增长到出现java.lang.OutOfMemoryError为止.经过分析Java内存泄漏是破坏系统的主要因素.这里与大家分享我们在开发过程中遇到的Java内存泄漏的检测和处理解决过程.JAVA中文站社区门户 k^N8ki
JAVA中文站社区门户 u(s\'[~S"Y [l d
一. Java是如何管理内存JAVA中文站社区门户{3Um xc&WK
z9o#Q `3y"J XuWt 为了判断Java中是否有内存泄露,我们首先必须了解Java是如何管理内存的.Java的内存管理就是对象的分配和释放问题.在Java中,内存的分配是由程序完成的,而内存的释放是由垃圾收集器(Garbage Collection,GC)完成的,程序员不需要通过调用函数来释放内存,但它只能回收无用并且不再被其它对象引用的那些对象所占用的空间.
Y m,NVa@"C
Yn1B*dG(i2U U Java的内存垃圾回收机制是从程序的主要运行对象开始检查引用链,当遍历一遍后发现没有被引用的孤立对象就作为垃圾回收.GC为了能够正确释放对象,必须监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等,GC都需要进行监控.监视对象状态是为了更加准确地、及时地释放对象,而释放对象的根本原则就是该对象不再被引用.JAVA中文站社区门户q Ig q(]A:M
V^{b8` T 在Java中,这些无用的对象都由GC负责回收,因此程序员不需要考虑这部分的内存泄露.虽然,我们有几个函数可以访问GC,例如运行GC的函数System.gc(),但是根据Java语言规范定义,该函数不保证JVM的垃圾收集器一定会执行.因为不同的JVM实现者可能使用不同的算法管理GC.通常GC的线程的优先级别较低.JVM调用GC的策略也有很多种,有的是内存使用到达一定程度时,GC才开始工作,也有定时执行的,有的是平缓执行GC,有的是中断式执行GC.但通常来说,我们不需要关心这些.JAVA中文站社区门户~|bRs Z&`\d:xQ:k$q
JAVA中文站社区门户0g+~ x'a;T9J;Qe
二. 什么是Java中的内存泄露JAVA中文站社区门户%L'B,t-{ j4[$@J)j
JAVA中文站社区门户9H4q }+m.Ti*}H
导致内存泄漏主要的原因是,先前申请了内存空间而忘记了释放.如果程序中存在对无用对象的引用,那么这些对象就会驻留内存,消耗内存,因为无法让垃圾回收器GC验证这些对象是否不再需要.如果存在对象的引用,这个对象就被定义为"有效的活动",同时不会被释放.要确定对象所占内存将被回收,我们就要务必确认该对象不再会被使用.典型的做法就是把对象数据成员设为null或者从集合中移除该对象.但当局部变量不需要时,不需明显的设为null,因为一个方法执行完毕时,这些引用会自动被清理.JAVA中文站社区门户*N\&H9OmS
pnkfQ\)o 在Java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是有被引用的,即在有向树形图中,存在树枝通路可以与其相连;其次,这些对象是无用的,即程序以后不会再使用这些对象.如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存.JAVA中文站社区门户t/O(kc6eV{R
,X7G6d f0v2Mz V 这里引用一个常看到的例子,在下面的代码中,循环申请Object对象,并将所申请的对象放入一个Vector中,如果仅仅释放对象本身,但因为Vector仍然引用该对象,所以这个对象对GC来说是不可回收的.因此,如果对象加入到Vector后,还必须从Vector中删除,最简单的方法就是将Vector对象设置为null.实际上这些对象已经是无用的,但还被引用,GC就无能为力了(事实上GC认为它还有用),这一点是导致内存泄漏最重要的原因. 再引用另一个例子来说明Java的内存泄漏.假设有一个日志类Logger,其提供一个静态的log(String msg),任何其它类都可以调用Logger.Log(message)来将message的内容记录到系统的日志文件中.JAVA中文站社区门户f#FH-w b+IN
JAVA中文站社区门户x*mL@Orr
Logger类有一个类型为HashMap的静态变量temp,每次在执行log(message)的时候,都首先将message的值写入temp中(以当前线程+当前时间为键),在退出之前再从temp中将以当前线程和当前时间为键的条目删除.注意,这里当前时间是不断变化的,所以log在退出之前执行删除条目的操作并不能删除执行之初写入的条目.这样,任何一个作为参数传给log的字符串最终由于被Logger的静态变量temp引用,而无法得到回收,这种对象保持就是我们所说的Java内存泄漏. 总的来说,内存管理中的内存泄漏产生的主要原因:保留下来却永远不再使用的对象引用.
一些在网上搜刮的内容,自己也没有什么好的办法。
1.泄漏的分类, 以发生的方式来分类,内存泄漏可以分为4类:
(1). 常发性内存泄漏。发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。
(2). 偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。
(3). 一次性内存泄漏。发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏。比如,在类的构造函数中分配内存,在析构函数中却没有释放该内存,所以内存泄漏只会发生一次。
(4). 隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。
从用户使用程序的角度来看,内存泄漏本身不会产生什么危害,作为一般的用户,根本感觉不到内存泄漏的存在。真正有危害的是内存泄漏的堆积,这会最终消耗尽系统所有的内存。从这个角度来说,一次性内存泄漏并没有什么危害,因为它不会堆积,而隐式内存泄漏危害性则非常大,因为较之于常发性和偶发性内存泄漏它更难被检测到。
2.内存泄漏的表现
内存泄漏或者是说,资源耗尽后,系统会表现出什么现象哪?
cpu资源耗尽:估计是机器没有反应了,键盘,鼠标,以及网络等等。这个在windows上经常看见,特别是中了毒。
进程id耗尽:没法创建新的进程了,串口或者telnet都没法创建了。
硬盘耗尽: 机器要死了,交换内存没法用,日志也没法用了,死是很正常的。
内存泄漏或者内存耗尽:新的连接无法创建,free的内存比较少。发生内存泄漏的程序很多,但是要想产生一定的后果,就需要这个进程是无限循环的,是个服务进程。当然,内核也是无限循环的,所以,如果内核发生了内存泄漏,情况就更加不妙。内存泄漏是一种很难定位和跟踪的错误,目前还没看到有什么好用的工具(当然,用户空间有一些工具,有静态分析的,也会动态分析的,但是找内核的内存泄漏,没有好的开源工具)
内存泄漏和对象的引用计数有很大的关系,再加上c/c 都没有自动的垃圾回收机制,如果没有手动释放内存,问题就会出现。如果要避免这个问题,还是要从代码上入手,良好的编码习惯和规范,是避免错误的不二法门。
3.内存泄漏的检测
内存泄漏检测工具介绍: http://bbs.51testing.com/thread-87138-1-1.html,只是列出了各个工具,具体实践没有。
另外网上还有很多针对java和c++等的内存泄漏的文章
Java内存泄漏:http://www.blogjava.net/galaxyp/archive/2006/04/28/43724.html
VC6.0内存泄漏检测:http://leoman95.spaces.live.com/blog/cns!87f442ce8fa434b1!131.entry
LR中:如果Process\Private Bytes计数器和Process\Working Set计数器的值持续升高,同时Memory\Available bytes计数器的值却持续降低的话,说明很有可能是存在内存泄漏。
发表评论
-
java 反编译的好工具
2013-07-19 17:27 806不错的反编译工具 支撑jdk1.6 -
eclipse禁止自动更新
2013-07-10 09:04 15471. window --> preferences -- ... -
Eclipse中设置编码的方式
2013-07-09 17:17 933如果要使插件开发应用 ... -
Android原理---不需要关闭后台运行程序
2012-11-26 14:06 1035不用在意剩余内存的大 ... -
java传值还是传引用
2009-09-23 18:31 1362JAVA中的传递都是值传递吗?有没有引用传递呢? 在回答这 ... -
Java的内存泄漏
2009-05-05 19:45 988Java的一个重要优点就是通过垃圾收集器(Garbage Co ...
相关推荐
内存泄露测试文档,提供两种不同方法供内存泄露测试。
良好的内存管理不仅能够提升程序性能,还能避免各种潜在的问题,如内存泄漏。本文将从C++内存泄露的基础概念入手,结合具体的代码示例,深入探讨内存泄露的原因、检测方法以及如何预防内存泄露。 #### 二、内存泄露...
本资料包提供了一种常用且强大的内存泄漏检测工具——Valgrind,以及其安装教程、使用示例和详细说明文档。 Valgrind是一款开源的动态分析工具,主要用于检测C和C++程序中的内存错误,包括内存泄漏、未初始化的内存...
**JProfiler内存泄漏查找文档** JProfiler是一款强大的Java性能分析工具,特别在内存泄漏检测方面有着显著的功效。本文档将详细介绍如何使用JProfiler来定位和解决内存泄漏问题,旨在帮助开发者提升应用程序的性能...
使用前,需要先解压"leakfinder.tar",然后按照工具的文档或指南进行配置和运行,以检查项目的内存泄漏情况。 总之,理解并有效利用内存泄漏检测器是C++开发中必不可少的技能,它能帮助我们编写更加健壮、高效的...
### Windows 下 C++ 内存泄露检测工具使用详解 在 Windows 平台下进行 C++ 开发时,内存管理是一项非常重要的任务。由于 C++ 语言本身的特性,开发者需要手动管理内存分配与释放,这就很容易导致内存泄露的问题。...
内存泄漏是常见的编程错误,会导致程序占用的内存持续增长,最终可能导致系统资源耗尽。Visual Studio(VS)作为强大的开发环境,提供了一些内置工具来帮助开发者检测和定位内存泄漏。本文将深入探讨如何利用VS进行...
1. 内存泄漏的诊断:使用内存分析工具来发现潜在的内存泄漏源。例如,通过MAT分析heap dump来查找大对象和长期存活对象的模式。 2. 代码分析:定位代码中可能导致内存泄漏的部分。常见的内存泄漏场景包括静态集合的...
本文将深入探讨VC内存泄露检查的相关知识点,帮助开发者有效识别和解决这一问题。 1. **内存泄露的概念** 内存泄露是指程序在申请内存后,无法释放已申请的内存空间,一次小的内存泄露可能不明显,但如果长时间...
JProfiler对应用服务器内存泄漏问题诊断解决方案 一、概述 本文主要介绍如何使用 JProfiler 对应用服务器内存泄漏问题进行诊断和解决。内存泄漏是指应用服务器中的一种常见问题,表现为内存长期不合理占用,内存...
本文将基于标题“[android]内存分析相关文档”和描述中的博文链接,深入探讨Android应用的内存管理,特别是内存泄露及其规避策略。 首先,我们要理解Android内存管理的基本原理。Android系统使用Dalvik或ART...
VLD是一款开源的、针对Visual Studio的内存泄漏检测工具。它能集成到Visual Studio中,帮助开发者在调试阶段发现并定位内存泄露的位置。VLD的工作原理是通过钩子函数拦截C++标准库中的动态内存分配和释放函数,如`...
VLD是一款用于VisualC++的免费内存泄漏检查工具。可以在codeproject.com网站上找到,相比其它的内存泄漏哦给你根据,他在检查内存泄漏的同事,还具有如下特点: 1) 可以得到内存泄漏点的调用堆栈,如果可以的话,...
内存泄漏是程序运行过程中,不再使用的内存没有被正确释放,导致系统资源持续占用,从而影响程序性能甚至系统稳定性。在JavaScript这种动态语言中,由于其内存管理机制,开发者容易无意间造成内存泄漏。针对这个问题...
总结来说,"leakdiag"内存泄露检查工具是开发过程中不可或缺的辅助工具,它通过详细的报告和精确的定位,帮助开发者快速识别和解决内存泄漏问题,确保软件的稳定性和高效运行。对于任何关心内存管理的开发团队,掌握...
在IT行业中,CSS样式和内存泄漏是两个关键的议题,特别是在网页开发中。CSS样式兼容性涉及不同浏览器如何解析和显示CSS代码,而内存泄漏则关乎程序效率和资源管理。 首先,我们来看CSS样式的兼容性。不同的浏览器,...
本文档将深入探讨C++内存泄漏检测的原理,并提供相关的代码示例。 首先,我们需要理解什么是内存泄漏。在C++中,程序员需要手动分配和释放内存。当动态分配的内存不再使用但没有被正确释放时,就会发生内存泄漏。...
2. Purify是另一种性能分析工具,虽然文档中没有详细说明,但它也是业界公认的用于检测内存泄漏的专业工具之一。 3. JDK自带的工具,如jmap和jvisualvm,也可以用来检测Java应用程序的内存使用情况。jmap可以生成堆...
内存泄漏是程序开发中常见的问题,它发生在程序分配了内存但未能正确释放,导致系统资源逐渐耗尽。在C++编程中,这个问题尤为突出,因为C++程序员需要手动管理内存。"检查代码内存泄露"这一主题是针对如何发现并解决...
在C/C++编程中,内存泄漏是一个常见的问题,它会导致程序效率下降,甚至崩溃。为了解决这个问题,程序员可以利用一些内存泄漏检测工具。本文将详细介绍几款实用的C/C++内存泄漏检测工具,帮助程序员有效地找出并修复...