胡八一及Java(五):java記憶體回收
當一個物件在堆記憶體中執行時,有以下三種狀態:
可達狀態:當一個物件有一個以上的引用變數引用它,程式可以通過引用變數來呼叫物件的屬性和方法。在有向圖中,這就屬於可達。
可恢復狀態:如果一個物件沒有任何變數再引用它,那麼它將先進入可恢復狀態。系統的垃圾回收機制準備回收該物件所佔的記憶體,在回收該物件之前,系統會呼叫可恢復狀態的物件finalize方法進行資源清理,如果系統呼叫該方法讓物件有一個以上的變數重新引用它,則這個物件就會再變成可達狀態,否則變成不可達狀態。
不可達狀態:當物件沒有引用變數引用它時,而且呼叫finalize方法也沒有使其變為可達狀態,則系統將會回收其物件所佔的記憶體空間。
一個物件可以被一個方法的例項變數引用,可以被其它類的類變數引用,也可以被其他物件的例項物件引用。當某個物件被其他類的類變數所引用時,只有當這個類銷燬時,該物件才會進入可銷燬狀態;當某個物件被其他物件引用時,只有這個物件銷燬時或進入不可達狀態時,這個物件才會進入不可達狀態。
java物件物件的引用有四種:強引用、軟引用、弱引用,虛引用
其中我們程式設計經常可以採納的有兩種:強引用、軟引用。
強引用:程式建立一個物件,並把這個物件賦給一個引用變數。這種引用狀態是Java程式設計中最常見的,被強引用的物件絕不會被垃圾回收機制回收,即使記憶體非常緊張,jvm也不會回收這種強引用所引用的物件,所以這也是Java記憶體洩漏的主要原因之一。
(記憶體洩漏:程式執行過程中會不斷地分配記憶體空間,那些不在使用的記憶體空間將會被回收,從而保證系統可以再次使用這些記憶體,如果存在無用的記憶體沒有被回收回來,那就是記憶體洩漏)
軟引用:通過SoftReference類來實現,當系統的空間足夠時,物件不會被回收,當記憶體緊張時,記憶體才會回收物件。
package aaaaa; import java.lang.ref.SoftReference; class Person { String name; int age; public Person(String name,int age){ this.age=age; this.name=name; } public String toString(){ return "Person[name]:="+name+", age="+age; } } public class Test{ public static void main(String args[]) throws Exception{ SoftReference<Person>[] people =new SoftReference[100000]; for(int i =0;i<people.length;i++){ people[i] =new SoftReference<Person>(new Person("名字"+i,(i+1)*4%100)); } System.out.println(people[2].get()); System.out.println(people[4].get()); //通知系統進行垃圾回收 System.gc(); System.runFinalization(); //垃圾回收機制執行會, System.out.println(people[2].get()); System.out.println(people[4].get()); } }
執行結果:
Person[name]:=名字2, age=12
Person[name]:=名字4, age=20
Person[name]:=名字2, age=12
Person[name]:=名字4, age=20
此時記憶體沒有達到緊張狀態,所以沒有清除2和4物件,可通過在cmd中為JVM指定較小的堆記憶體來達到效果,如下
java -Xmx2m -Xms2m 類名
Java記憶體管理小技巧:
1、儘量使用直接量
當使用字串,Byte,Short,Integer,Float,Double,Boolean,Character包裝類的例項時不應該使用new來建立物件,而應該直接
使用
如:
String a = new String("hello");
這種方式會建立一個“hello"字串,而且JVM的字元緩衝池會快取這個字串,除此之外,a所引用的String物件底層還包含一個char陣列,char數組裡依次存放了hello 的每個字元。
String a ="hello";
而這種就不會產生char陣列。
2、使用StrinBuilder和StringBuffer來進行字串連線
String 代表字元序列不可變字串,而StringBuffer和StringBuilder代表字元序列可變字串。
如果使用String字串來連線字串,則會產生大量零時字串。
3、儘早釋放無用物件,將物件顯式的設定為null
public void info(){
Object obj =new Object();
System.out.println(obj.toString());
System.out.println(obj.hashCode());
obj =null;
//下面是一些耗記憶體的操作,完全需要釋放上面物件的記憶體空間
....
}
4、儘量少用靜態變數
靜態變數生命週期與類同步,當遇到常駐記憶體的class ,一旦被建立,就會常駐記憶體。
5、避免在經常呼叫的方法中,迴圈中建立Java物件
6、快取經常使用的物件
7、儘量不要使用finalize方法
8、考慮使用SoftReference