一、本文參考:
1.《深入理解java虛擬機 JVM高級特性與最佳實踐》
二、對象已死的判定方法
要進行JVM中對象回收首先要判斷對象是否已經死亡,判斷的方法有如下幾個:
1.引用計數法
給對象中添加一個引用計數器,每當有一個地方引用它時,計數器值就加1;當引用失效時,計數器值就減1;任何時刻 計數器為0的對象就是不可能再被使用的。
但是主流的java虛擬機里面沒有選用引用計數器算法來管理內存,其中最主要的原因是它很難解決對象之間相互循環引用的問題。
2.可達性分析算法
這個算法的基本思想就是通過一系列的稱為“GC Roots"的對象作為起始點,從這些節點開始向下搜索,搜索所走過的路徑稱為引用鏈,當一個對象到GC Roots沒有任何引用鏈相連接時,則證明此對象是不可用的。如下圖所示,對象object5、object6、object7雖然互相有關聯,但是它們到GC Roots是不可達的,所以它們將會被判定為是可回收對象。


三、導致內存泄漏的情況及代碼
java 堆內存泄漏。是由於java對象不停創建但是沒有釋放對象引用導致的。
以下是關於java代碼,此代碼是引自
http://coderevisited.com/memory-leaks-in-java/
類com.code.revisited.memoryleaks.Stack提供了實現棧的一些方法,包括遍歷,入棧,出棧等操作。假設原來目的是為了現實使用(當然這里是為了解釋內存泄漏)。
package com.code.revisited.memoryleaks; import java.util.Iterator; import java.util.NoSuchElementException; /** * @author sureshsajja * */ public class Stack<E> implements Iterable<E> { private int N; private E[] array; @SuppressWarnings("unchecked") public Stack(int capacity) { array = (E[]) new Object[capacity]; } @Override public Iterator<E> iterator() { return new StackIterator(); } private class StackIterator implements Iterator<E> { private int i = N - 1; @Override public boolean hasNext() { return i >= 0; } @Override public E next() { if (!hasNext()) { throw new NoSuchElementException(); } return array[i--]; } @Override public void remove() { throw new UnsupportedOperationException(); } } public void push(E item) { if (isFull()) { throw new RuntimeException("Stack overflow"); } array[N++] = item; } public E pop() { if (isEmpty()) throw new RuntimeException("Stack underflow"); E item = array[--N]; return item; } public boolean isEmpty() { return N == 0; } public int size() { return N; } public boolean isFull() { return N == array.length; } public E peek() { if (isEmpty()) throw new RuntimeException("Stack underflow"); return array[N - 1]; } }
類com.code.revisited.memoryleaks.StackTest用於執行棧操作。要進行入棧及出棧10000次操作,理想是入棧時分配堆內存,出棧后對象被回收。
package com.code.revisited.memoryleaks; /** * @author sureshsajja * */ public class StackTest { /** * @param args */ public static void main(String[] args) { Stack<Integer> s = new Stack<Integer>(10000); for (int i = 0; i < 10000; i++) { s.push(i); } while (!s.isEmpty()) { s.pop(); } while (true ) { // do something } } }
執行開始。我們使用VisualVM進行觀察。為了更明顯一些,將棧操作部分代碼注釋也執行一下。
package com.code.revisited.memoryleaks; /** * @author sureshsajja * */ public class StackTest { /** * @param args */ public static void main(String[] args) { // Stack<Integer> s = new Stack<Integer>(10000); // for ( int i = 0; i < 10000; i++) { // s.push(i); // } // // while (!s.isEmpty()) { // s.pop(); // } while (true ) { // do something } } }
把棧操作的設為1號,沒有棧操作的設置為2號,分別生成Heap Dump文件,我們看一下類實例的截圖:
首先是1號截圖

首先是2號截圖

顯然預期的棧操作出棧后並沒有釋放掉Integer對象的引用(實際上看代碼也知道),所以不會被GC回收。真正的實際情況這種引用將會很隱蔽,但是根本總是由於對象仍然被引用。
四、結語
本篇僅對java堆內存泄漏進行了簡單說明,下一篇將討論其他相關的內存泄漏。有不對的地方歡迎拍磚>_<