2021-05-12 14:32:11
java中垃圾回收的方法
Java是一門物件導向程式設計語言,不僅吸收了C++語言的各種優點,還摒棄了C++裡難以理解的多繼承、指標等概念,因此Java語言具有功能強大和簡單易用兩個特徵。Java語言作為靜態物件導向程式設計語言的代表,極好地實現了物件導向理論,允許程式員以優雅的思維方式進行複雜的程式設計。
Java具有簡單性、物件導向、分散式、健壯性、安全性、平台獨立與可移植性、多執行緒、動態性等特點。Java可以編寫桌面應用程式、Web應用程式、分散式系統和嵌入式系統應用程式等。
java的垃圾記憶體不需要程式程式碼來顯式地釋放,JVM在實現的時候都有一個由垃圾回收所管理的堆。垃圾回收是一種動態儲存管理技術,它自動地釋放不再被程式參照的物件,按照特定的垃圾收集演算法來實現資源自動回收的功能。
1
第一點:java垃圾回收機制。在Java中,當沒有物件參照指向原先分配給某個物件的記憶體時,該記憶體便成為垃圾。JVM的一個系統級執行緒會自動釋放該記憶體塊。垃圾收集意味著程式不再需要的物件是"無用資訊",這些資訊將被丟棄。當一個物件不再被參照的時候,記憶體回收它佔領的空間,以便空間被後來的新物件使用。?垃圾收集能自動釋放記憶體空間,減輕程式設計的負擔。這使Java 虛擬機器具有一些優點。?垃圾收集的一個潛在的缺點是它的開銷影響程式效能。Java虛擬機器必須追蹤執行程式中有用的物件, 而且最終釋放沒用的物件。這一個過程需要花費處理器的時間。其次垃圾收集演算法的不完備性,早先採用的某些垃圾收集演算法就不能保證100%收集到所有的廢棄記憶體。當然2
第二點:檢測垃圾演算法。
1、參照計數演算法(Reference Counting)
原理:給每個物件新增一參照計數器,每當有一個地方參照它,計數器+1 ,參照失效時就-1 。
分析:參照計數演算法很簡單高效。但是,現在主流的虛擬機器沒有選用參照計數演算法來管理記憶體,原因是它很難解決物件之間相互參照的問題。
public class ReferenceCountingGC{
public Object instance = null;
public static void testGC(){
ReferenceCountingGC objA = new ReferenceCountingGC ();
ReferenceCountingGC objB = new ReferenceCountingGC ();
objB.instance = objA;
objA.instance = objB;
objA = null;
objB = null;
System.gc();
}
}
此處objA,objB 不會回收,因為其還有其它參照
2、可達性分析演算法(Rearchability Analysis)
原理:以根集物件為起始點進行搜尋,如果有物件不可達的話,即是垃圾物件。這裡的根集一般包括java棧中參照的物件、方法區常良池中參照的物件、本地方法中參照的物件等。
3
第三點:物件的四種參照。
強參照 :建立一個物件並把這個物件直接賦給一個變數,不管系統資源多麼緊張,強參照的物件都不會被回收,即使他以後不會再用到。
軟參照 :通過SoftReference修飾的類,記憶體非常緊張的時候會被回收,其他時候不會被回收,在使用之前要判斷是否為null從而判斷他是否已經被回收了。
弱參照 :通過WeakReference修飾的類,不管記憶體是否足夠,系統垃圾回收時必定會回收。
虛參照 :不能單獨使用,主要是用於追蹤物件被垃圾回收的狀態。通過PhantomReference修飾和參照佇列ReferenceQueue類聯合使用實現。
如下所示:分別是強,軟,弱的測試,虛使用較少暫不介紹。
4
第四點:垃圾回收機制1、序列回收和並行回收:序列回收是不管系統有多少個CPU,始終只用一個CPU來執行垃圾回收操作;並行回收就是把整個回收工作拆分成多個部分,每個部分由一個CPU負責,從而讓多個CPU並行回收。並行回收的執行效率很高,但更複雜,記憶體會增加。/2、程式停止和並行執行 :顧名思義是在執行垃圾回收的同時會導致應用程式的暫停。並行執行垃圾回收雖不會導致應用程式的暫停,但需要解決和應用程式的執行衝突,因此系統開銷比較高,執行時需要更多的堆記憶體。3、標記不壓縮:標記-清除要遍歷兩次。第一次先從根集開始存取所有可達物件,並將他們標記為可達狀態。第二次遍歷整個記憶體區域,對不達狀態的物件進行回收處理。這種回收方式不壓縮5
第五點:堆記憶體的分代回收
1、年輕世代
採用複制式回收演算法,劃分兩個區域,分別是E 區和 S 區。大多數物件先分配到Eden區,記憶體大的物件會直接被分配到老年代中。S 區又分Form、To兩個小區,一個用來儲存物件,另一個是空的;每次進行年輕代垃圾回收的時候,就把E大區和From小區中的可達物件都複製到To區域中,一些生存時間長的就直接複製到了老年代。最後,清理回收E大區和From小區的記憶體空間,原來的To空間變為From空間,原來的From空間變為To空間。
2、年老世代
回收機制 :採用標記壓縮演算法回收。
物件來源 :物件大直接進入老年代、Young代中生存時間長的可達物件。
回收頻率 :因為很少物件會死掉,所以執行頻率不高,而且需要較長時間來完成。
3、永久世代
用 途 :用來裝載Class,方法等資訊,預設為64M(Android的執行時應用分配的記憶體),不會被回收。
物件來源 :像Hibernate,Spring這類喜歡AOP動態生成類的框架,往往會生成大量的動態代理類,因此我們經常遇到java.lang.OutOfMemoryError:PermGen space的錯誤,這就是Permanent代記憶體耗盡所導致的錯誤。
回收頻率 :不會被回收。
6
第六點:垃圾回收的方法
1.物件被賦值null,或者手動釋放
User user = new User();user = null;
或者
System.gc();和Runtime.getRuntime().gc();等價
2、弱參照
如果一個物件具有弱參照,在GC執行緒掃描記憶體區域的過程中,不管當前記憶體空間足夠與否,都會回收記憶體,使用弱參照 構建非敏感資料的快取。
弱參照申明:
WeakReferenceweakReference=new WeakReference(new User());
3、虛參照
如果一個物件僅持有虛參照,在任何時候都可能被垃圾回收,虛參照與軟參照和弱參照的一個區別在於:虛參照必須和參照佇列聯合使用,虛參照主要用來跟蹤物件 被垃圾回收的活動。
虛參照申明:
PhantomReference phantomReference=new PhantomReference(new User(),new ReferenceQueue());
7
小結?實際開發中常用的小技巧:1、盡量使用直接變數,例如:String javaStr = 「XXX」;2、使用 StringBuilder 和 StringBuffer 進行字串連線等操作;3、儘早釋放無用物件;4、盡量少使用靜態變數;5、快取常用的物件:可以使用開源的開源快取實現,例如:OSCache,Ehcache;6、盡量不使用 finalize() 方法;7、在必要的時候可以考慮使用軟參照 SoftReference。相關文章