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就經常用到對象緩衝技術。