Java内存泄露的理解与解决(转)
转自:http://www.blogjava.net/zh-weir/archive/2011/02/23/345007.html
Java内存管理机制
在C++语言中,如果需要动态分配一块内存,程序员需要负责这块内存的整个生命周期。从申请分配、到使用、再到最后的释放。这样的过程非常灵活,但是却十分繁琐,程序员很容易由于疏忽而忘记释放内存,从而导致内存的泄露。Java语言对内存管理做了自己的优化,这就是垃圾回收机制。Java的几乎所有内存对象都是在堆内存上分配(基本数据类型除外),然后由GC(garbage collection)负责自动回收不再使用的内存。
上面是Java内存管理机制的基本情况。但是如果仅仅理解到这里,我们在实际的项目开发中仍然会遇到内存泄漏的问题。也许有人表示怀疑,既然Java的垃圾回收机制能够自动的回收内存,怎么还会出现内存泄漏的情况呢?这个问题,我们需要知道GC在什么时候回收内存对象,什么样的内存对象会被GC认为是“不再使用”的。
Java中对内存对象的访问,使用的是引用的方式。在Java代码中我们维护一个内存对象的引用变量,通过这个引用变量的值,我们可以访问到对应的内存地址中的内存对象空间。在Java程序中,这个引用变量本身既可以存放堆内存中,又可以放在代码栈的内存中(与基本数据类型相同)。GC线程会从代码栈中的引用变量开始跟踪,从而判定哪些内存是正在使用的。如果GC线程通过这种方式,无法跟踪到某一块堆内存,那么GC就认为这块内存将不再使用了(因为代码中已经无法访问这块内存了)。
通过这种有向图的内存管理方式,当一个内存对象失去了所有的引用之后,GC就可以将其回收。反过来说,如果这个对象还存在引用,那么它将不会被GC回收,哪怕是Java虚拟机抛出OutOfMemoryError。
Java内存泄露
一般来说内存泄漏有两种情况。一种情况如在C/C++语言中的,在堆中的分配的内存,在没有将其释放掉的时候,就将所有能访问这块内存的方式都删掉(如指针重新赋值);另一种情况则是在内存对象明明已经不需要的时候,还仍然保留着这块内存和它的访问方式(引用)。第一种情况,在Java中已经由于垃圾回收机制的引入,得到了很好的解决。所以,Java中的内存泄漏,主要指的是第二种情况。
可能光说概念太抽象了,大家可以看一下这样的例子:
Vector v=new Vector(10); for (int i=1;i<100; i++){ Object o=new Object(); v.add(o); o=null; }public class FileSearch{ private byte[] content; private File mFile; public FileSearch(File file){ mFile = file; } public boolean hasString(String str){ int size = getFileSize(mFile); content = new byte[size]; loadFile(mFile, content); String s = new String(content); return s.contains(str); }}package com.***.widget;//: SoftHashMap.java import java.util.*;import java.lang.ref.*;import android.util.Log;public class SoftHashMap extends AbstractMap {/** The internal HashMap that will hold the SoftReference. */private final Map hash = new HashMap();/** The number of "hard" references to hold internally. */private final int HARD_SIZE;/** The FIFO list of hard references, order of last access. */private final LinkedList hardCache = new LinkedList();/** Reference queue for cleared SoftReference objects. */private ReferenceQueue queue = new ReferenceQueue();// Strong Reference numberpublic SoftHashMap() {this(100);}public SoftHashMap(int hardSize) {HARD_SIZE = hardSize;}public Object get(Object key) {Object result = null;// We get the SoftReference represented by that keySoftReference soft_ref = (SoftReference) hash.get(key);if (soft_ref != null) {// From the SoftReference we get the value, which can be// null if it was not in the map, or it was removed in// the processQueue() method defined belowresult = soft_ref.get();if (result == null) {// If the value has been garbage collected, remove the// entry from the HashMap.hash.remove(key);} else {// We now add this object to the beginning of the hard// reference queue. One reference can occur more than// once, because lookups of the FIFO queue are slow, so// we don't want to search through it each time to remove// duplicates.// keep recent use object in memoryhardCache.addFirst(result);if (hardCache.size() > HARD_SIZE) {// Remove the last entry if list longer than HARD_SIZEhardCache.removeLast();}}}return result;}/** * We define our own subclass of SoftReference which contains not only the * value but also the key to make it easier to find the entry in the HashMap * after it's been garbage collected. */private static class SoftValue extends SoftReference {private final Object key; // always make data member final/** * Did you know that an outer class can access private data members and * methods of an inner class? I didn't know that! I thought it was only * the inner class who could access the outer class's private * information. An outer class can also access private members of an * inner class inside its inner class. */private SoftValue(Object k, Object key, ReferenceQueue q) {super(k, q);this.key = key;}}/** * Here we go through the ReferenceQueue and remove garbage collected * SoftValue objects from the HashMap by looking them up using the * SoftValue.key data member. */public void processQueue() {SoftValue sv;while ((sv = (SoftValue) queue.poll()) != null) {if (sv.get() == null) { Log.e("processQueue", "null");} else { Log.e("processQueue", "Not null");}hash.remove(sv.key); // we can access private data!Log.e("SoftHashMap", "release " + sv.key);}}/** * Here we put the key, value pair into the HashMap using a SoftValue * object. */public Object put(Object key, Object value) {processQueue(); // throw out garbage collected values firstLog.e("SoftHashMap", "put into " + key);return hash.put(key, new SoftValue(value, key, queue));}public Object remove(Object key) {processQueue(); // throw out garbage collected values firstreturn hash.remove(key);}public void clear() {hardCache.clear();processQueue(); // throw out garbage collected valueshash.clear();}public int size() {processQueue(); // throw out garbage collected values firstreturn hash.size();}public Set entrySet() {// no, no, you may NOT do that!!! GRRRthrow new UnsupportedOperationException();}}