简述Java内存泄露

来源:互联网 发布:网络恐怖主义案例 编辑:程序博客网 时间:2024/06/03 04:04
翻译人员: 铁锚
翻译时间: 2013年11月4日
原文链接: The Introduction of Memory Leaks

内存管理一直是Java 所鼓吹的强大优点。开发者只需要简单地创建对象,而Java的垃圾收集器将会自动管理内存空间的分配和释放.但在很多情况下,事情并不那么简单,在 Java程序中总是会频繁地发生内存泄露(Memory Leaks).
本文阐述什么是内存泄露,为什么会泄露,以及如何防止内存泄露。

内存泄露是什么?
内存泄露的定义: 当某些对象不再被应用程序所使用,但是由于仍然被引用而导致垃圾收集器不能释放(Remove,移除)他们.
用白话来说就是: 该回收的内存没被回收,最后因为内存不够用而导致程序报错。

要理解这个定义,我们需要理解内存中的对象状态. 下图展示了什么是不使用的部分,以及未被引用的部分。


图1

从图中可以看出,内存中存在着 有引用的对象,和无引用的对象. 无引用的对象将被垃圾收集器所回收,而有引用的对象则不会被当做垃圾收集. 因为没有任何其他对象所引用,所以无引用对象一定是不再使用的。 但是有一部分无用对象仍然被(无意中)引用着。这就是发生内存泄露的根源.

为什么内存会泄露?
让我们看下面的实例来了解为什么内存会泄露. 在下面的情境中,对象A引用了对象B. A的生命周期(t1 - t4) 比 B的(t2 - t3)要长得多. 当对象B在应用程序逻辑中不会再被使用以后, 对象 A 仍然持有着 B的引用. (根据虚拟机规范)在这种情况下垃圾收集器不能将 B 从内存中释放. 这种情况很可能会引起内存问题,假若A 还持有着其他对象的引用,那么这些被引用的(无用)对象都不会被回收,并占用着内存空间.
甚至有可能 B 也持有一大堆其他对象的引用。 这些对象由于被 B 所引用,也不会被垃圾收集器所回收. 所有这些无用的对象将消耗大量宝贵的内存空间。

图2

怎么防止内存泄露?
要防止内存泄露,下面是一些快速上手的实用技巧:
1. 当心集合类,比如 HashMap,ArrayList等,因为这是最容易发生内存泄露的地方.当集合对象被声明为static时,他们的生命周期一般和整个应用程序一样长。
2. 注意事件监听和回调.当注册的监听器不再使用以后,如果没有被注销,那么很可能会发生内存泄露.
3. "当一个类自己管理其内存空间时,程序员应该注意内存泄露." 常常是一个对象的成员变量需要被置为null 时仍然指向其他对象,

一个小测验: 为什么 JDK6 中的 substring() 方法有可能导致内存泄露?
要回答这个问题,请参考: JDK6和JDK7中String的substring()方法及其差异

参考资料:
[1] Bloch, Joshua. Effective java. Addison-Wesley Professional, 2008.
[2] IBM Developer Work. http://www.ibm.com/developerworks/library/j-leaks/

相关文章:
1.  

Java 对象在堆中的内存结构


2.  Java 数组在内存中的结构
3. How is an array stored in memory and brought to cache?

4. Java String 常见问题


补充说明:

严格意义上内存泄露的意思是内存不可达,就是说这片内存你即引用不到也没办法重新分配,c和c++里的确很容易有这种情况,最简单的比如在一个局部域里 int *a = malloc(10)*sizeof(int);,当走出这个局部域后a不能使用了,但10*sizeof(int)这片内存也没有任何机制去回收。至于java中的内存泄露,严格来讲不是内存泄露,只是从程序员的角度来讲的,程序员编程习惯不好导致很多内存即便不用了也被一直引用着,内存越来越少,看起来像是内存泄露了。


例子:

Java内存泄露
 
一般来说内存泄漏有两种情况。一种情况如在C/C++ 语言中的,在堆中的分配的内存,在没有将其释放掉的时候,就将所有能访问这块内存的方式都删掉(如指针重新赋值);另一种情况则是在内存对象明明已经不需要的时候,还仍然保留着这块内存和它的访问方式(引用)。第一种情况,在 Java 中已经由于垃圾回收机制的引入,得到了很好的解决。所以, Java 中的内存泄漏,主要指的是第二种情况。
 
    可能光说概念太抽象了,大家可以看一下这样的例子:
  1. Vector v = new  Vector( 10 );  
  2. for  ( int  i = 1 ;i < 100 ; i ++ ){  
  3. Object o = new  Object();  
  4. v.add(o);  
  5. o = null ;  

在这个例子中,代码栈中存在Vector 对象的引用 v 和 Object 对象的引用 o 。在 For 循环中,我们不断的生成新的对象,然后将其添加到 Vector 对象中,之后将 o 引用置空。问题是当 o 引用被置空后,如果发生 GC ,我们创建的 Object 对象是否能够被 GC 回收呢?答案是否定的。因为, GC 在跟踪代码栈中的引用时,会发现 v 引用,而继续往下跟踪,就会发现 v 引用指向的内存空间中又存在指向 Object 对象的引用。也就是说尽管 o 引用已经被置空,但是 Object 对象仍然存在其他的引用,是可以被访问到的,所以 GC 无法将其释放掉。如果在此循环之后, Object 对象对程序已经没有任何作用,那么我们就认为此 Java 程序发生了内存泄漏。

尽管对于C/C++ 中的内存泄露情况来说, Java 内存泄露导致的破坏性小,除了少数情况会出现程序崩溃的情况外,大多数情况下程序仍然能正常运行。但是,在移动设备对于内存和 CPU都有较严格的限制的情况下, Java 的内存溢出会导致程序效率低下、占用大量不需要的内存等问题。这将导致整个机器性能变差,严重的也会引起抛出 OutOfMemoryError ,导致程序崩溃。

一般情况下内存泄漏的避免

在不涉及复杂数据结构的一般情况下,Java 的内存泄露表现为一个内存对象的生命周期超出了程序需要它的时间长度。我们有时也将其称为“对象游离”。

例如:

  1. public class FileSearch{  
  2.       private byte [] content;  
  3.       private File mFile;  
  4.      public FileSearch(File file){  
  5.       mFile = file;  
  6.       }  
  7.      public boolean hasString(String str){  
  8.          int size = getFileSize(mFile);  
  9.         content =  new  byte [size];  
  10.          loadFile(mFile, content);  
  11.          String s =  new String(content);  
  12.          return s.contains(str);  
  13.      }  

在这段代码中,FileSearch 类中有一个函数 hasString ,用来判断文档中是否含有指定的字符串。流程是先将mFile 加载到内存中,然后进行判断。但是,这里的问题是,将 content 声明为了实例变量,而不是本地变量。于是,在此函数返回之后,内存中仍然存在整个文件的数据。而很明显,这些数据我们后续是不再需要的,这就造成了内存的无故浪费。

要避免这种情况下的内存泄露,要求我们以C/C++ 的内存管理思维来管理自己分配的内存。第一,是在声明对象引用之前,明确内存对象的有效作用域。在一个函数内有效的内存对象,应该声明为 local 变量,与类实例生命周期相同的要声明为实例变量……以此类推。第二,在内存对象不再需要时,记得手动将其引用置空。

复杂数据结构中的内存泄露问题

在实际的项目中,我们经常用到一些较为复杂的数据结构用于缓存程序运行过程中需要的数据信息。有时,由于数据结构过于复杂,或者我们存在一些特殊的需求(例如,在内存允许的情况下,尽可能多的缓存信息来提高程序的运行速度等情况),我们很难对数据结构中数据的生命周期作出明确的界定。这个时候,我们可以使用Java 中一种特殊的机制来达到防止内存泄露的目的。

之前我们介绍过,Java 的 GC 机制是建立在跟踪内存的引用机制上的。而在此之前,我们所使用的引用都只是定义一个“ Object o; ”这样形式的。事实上,这只是 Java 引用机制中的一种默认情况,除此之外,还有其他的一些引用方式。通过使用这些特殊的引用机制,配合 GC 机制,就可以达到一些我们需要的效果。


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 孩子脾气特别倔强怎么办 没有规矩的孩子怎么办 孩子情商太低怎么办 4岁宝宝缺乏自信怎么办 孩子害羞不自信怎么办 说话脸红的人怎么办 胆小自卑不自信怎么办 孩子爱挑衣服怎么办 厌学症的孩子怎么办 小孩太自死了怎么办 宝宝老是得肺炎怎么办 社保卡断档了怎么办 北京儿童怎么办医保卡 股票开户不用了怎么办 高中生玩游戏父母怎么办 高中生爱玩游戏怎么办 宝贝发烧不退烧怎么办 孩子行笔速度慢怎么办 孩子不会读英语怎么办 太注意小孩考试成绩怎么办 二年级成绩很差怎么办 班级差生应该怎么办 班级差生太多怎么办 初三孩子英语差怎么办 初三英语基础差怎么办 学生学习效率低怎么办 摔伤后皮下血肿怎么办 孩子学习效率低怎么办 很多作业没写完怎么办? 小学孩子成绩差怎么办 孩子脚上起水泡怎么办 小学一年级体育课哑铃怎么办 托福成绩失效后怎么办 去外国旅游不适应怎么办 一年级语文成绩差怎么办 一年级小孩成绩差怎么办 一年级小孩基础差怎么办 一年级小孩语文差怎么办 一年级小孩数学差怎么办 一年级小孩理解能力差怎么办 一年级小孩作文差怎么办