一、本文參考:
1.《深入理解java虛擬機 JVM高級特性與最佳實踐》
2.http://coderevisited.com/memory-leaks-in-java/
二、對象已死的判定方法
要進行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];}@Overridepublic Iterator<E> iterator() {return new StackIterator();}private class StackIterator implements Iterator<E> {private int i = N - 1;@Overridepublic boolean hasNext() {return i >= 0;}@Overridepublic E next() {if (!hasNext()) {throw new NoSuchElementException();}return array[i--];}@Overridepublic 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堆內存泄漏進行了簡單說明,下一篇將討論其他相關的內存泄漏。有不對的地方歡迎拍磚>_<