JAVA的垃圾回收機制,讓許多程序員覺得內存管理不是很重要,但是內存內存泄露的事情恰恰這樣的疏忽而發生,特別是對於Android開發,內存管理更為重要,養成良好的習慣,有利於避免內存的泄漏.
對象的幾種狀態:
這里可以把許多對象和引用看成是有向圖,頂點可以是對象也可以是引用,引用關系就是有向邊。
-
可達狀態:對象創建的時候,有引用指向它,這個時候在對象和引用之間建立了引用關系,即由引用發射有向邊指向對象,這個對象就是出於可達狀態
-
可恢復狀態:當引用不指向一個對象的時候,該對象就處於可恢復狀態,這時候在系統回收該對象之前,會調用finalize方法進行資源清理,如果調用這個方法后能重新讓引用變量去引用他,那么他又恢復到可達狀態,不然會變成不可達狀態。
-
不可達狀態:當對象和引用變量失去了引用關系,並且調用了finalize方法后,不能恢復到可達狀態,那么將永久性失去引用,此時系統才會真正去回收對象占用的內存。
何為內存泄漏:
內存泄漏就是程序運行中,java垃圾回收機制,會對一些不適用的內存進行回收,然后再重新分配,保證系統能再次用到這些內存,Java中所有不可達狀態都會被回收,但是一些處於可達狀態,但是程序卻再也不會用到的對象(占着茅坑不拉屎),這些內存就無法被回收,他們對於程序員來說已經沒用,但是對於垃圾回收機制來說,他們是可達的,所以是“有用的”這些占用的內存就會出現內存泄漏。
用一個棧的例子說明:
package CrazyJava; import javax.management.RuntimeErrorException; /** * * @author ccf * */ public class neicun { /** * @param args */ class Stack { private Object[] elementData; private int size; private int capacityIncrement; public Stack(int initialCapacity) { elementData = new Object[initialCapacity]; } public Stack(int initialCapacity, int capacityIncrement) { elementData = new Object[initialCapacity]; this.capacityIncrement = capacityIncrement; } public void push(Object object) { ensureCapacity(); elementData[size++] = object;// 后加 } public Object pop() { if (size == 0) throw new RuntimeException("空棧異常"); Object ele = elementData[--size];// 這里為局部變量,當方法結束,局部變量會被回收 elementData[size] = null;// 消去強引用關系,避免產生內泄漏。 return ele;// 返回棧頂元素 size自減1個長度 } public int size() { return size; } private void ensureCapacity() { // TODO Auto-generated method stub // 數組已經滿了。進行擴容。 if (elementData.length == size) { Object[] oldElmentata = elementData; int newLength = 0; if (capacityIncrement > 0) { newLength = elementData.length + capacityIncrement; } else { newLength = (int) (elementData.length * 1.5); } elementData = new Object[newLength]; System.arraycopy(oldElmentata, 0, elementData, 0, size); } } } public static void main(String[] args) { // TODO Auto-generated method stub Stack stack = new neicun().new Stack(10); for (int i = 0; i < 10; i++) { stack.push("元素" + i); } for (int i = 0; i < 10; i++) { System.out.println(stack.pop()); } } }
代碼中pop() 函數實現出棧,其中
elementData[size] = null;切斷了元素和其引用的引用關系,這樣可以使元素進入不可達狀態,從能被系統回收,如果不切斷引用,該元素將一直可以可達狀態,就會常駐內存。
而針對上一句Object ele
= elementData[
--size];
就是為了暫存這個元素,用於作為return的返回值,有人會問這個為什么不會造成內存泄露,他們之間不是已經有了引用和被引用的關系,原因是這里的Object對象是局部變量,局部變量的生命周期跟方法生命周期一樣,該方法結束了,搞局部變量會被垃圾回收機制收回,所以這個擔心是沒必要的。
從以上看來,內存泄漏可能性還是很大的,強引用類型(以后的博文會講)使我們用得最多的類型,這類引用只有再失去引用的時候才會被回收,其他情況都是不能被回收的,所以良好的編程習慣是避免內存泄漏的好辦法
怎樣避免內存泄漏:
-
盡量多使用直接量
例如String類型
String a =“ccf” //采用直接量,JVM字符串池會緩存這個字符串
String b = new String(“ccf”); //但是直接調用構造方法的話,因為String內部是基於數組,所以會產生 字符數組存儲ccf三個字符。 -
多使用StringBuilder和StringBuffer
使用StringBuilder和StringBuffer進行字符串的操作,可以減少使用String進行字符串操作產生的臨時字符串 -
釋放無用的對象引用,就像上面棧的 pop ()方法
-
少用靜態變量,靜態變量生命周期跟類一樣,為類加載到類卸載這段時間,也就是知道程序結束。
-
避免在循環和經常調用的方法創建對象,因為這些情況會產生大量對象,特別是像for循環這些。
-
緩存經常用到的對象,避免重復去創建相同對象,想android中Adapter中重寫getView就經常用到對象緩存技術。