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