標籤:記憶體溢出 ble 成功 介面 筆記 基本 http print java
Grabage Collection ? ? ?GC
GC要完畢的三件事情:哪些記憶體須要回收?什麼時候回收?怎樣回收?
記憶體運行時地區的各個部分中:
程式計數器、虛擬機器棧、本地方法棧這3個地區隨線程而生。隨線程而滅。棧中的棧幀隨著方法的進入和退出而有條不紊地運行著出棧和入棧的操作。
每個棧幀中分配多少記憶體基本上是在類結構確定下來時就已知的,因此,這幾個地區的記憶體配置和回收都具備確定性,在這幾個地區內就不需過多考慮回收的問題。由於方法結束或者線程結束時。記憶體自然就跟著回收了。而java堆和方法區就不一樣了。一個介面中多個實作類別須要的記憶體可能不一樣。一個方法中的多個分支須要的記憶體也可能不一樣,我們僅僅有在程式處於運行期間才知道會建立哪些對象,這部分記憶體的分配和回收是動態。我們的垃圾收集器所關注的就是這部分記憶體。
堆記憶體中存放這大量對象執行個體,垃圾收集器首先要推斷這些對象哪些是“活著的”哪些是“死去的”
引用計數器演算法給對象中加入一個引用計數器,每當被引用時,該計數器就加1。失去引用時計算機減一。
不論什麼時刻,計算機值為0就代表該對象是不能再被使用的。存在的問題:非常難解決對象間循環參考的問題
可達性分析演算法基本思想:通過一系列的稱為"GC Roots"的對象作為起點。從這些節點開始向下搜尋,搜尋所走過的路徑稱為引用鏈(Reference Chain),當一個對象到GC Roots沒有不論什麼引用鏈相連時,則證明此對象是停用。
java中。可作為GC Roots的對象包含以下幾種:1、虛擬機器棧(棧幀中的本地變數表)中引用的對象2、方法區中類靜態屬性引用的對象3、方法區中常量引用的對象4、本地方法棧中(即一般說的Native方法)引用的對象
不管哪種演算法都是和引用有關的,可是引用該怎樣理解呢?什麼樣的引用算是實用的,什麼樣的引用又是沒用的呢?
在JDK1.2後對引用進行了區分。這四種引用強度依次逐漸遞減:
強引用? ? ?指在程式總普遍存在的,如:Object obj = new Object(),這類引用。僅僅要強引用還在。垃圾收集器永遠不會回收掉被引用的對象。
軟引用? ? ?用來描寫敘述一些還實用可是非必須的對象。對於軟引用關聯著的對象。系統會在發生記憶體溢出異常之前,將會把這些對象列入回收範圍之中進行二次回收。假設這次回收後還沒有足夠的記憶體才會拋出記憶體溢出異常
弱引用? ? ?弱引用也是用來描寫敘述一些非必須對象的。它的強度比軟引用更弱一些,被弱引用關聯的對象。僅僅能存活到下一次垃圾收集發生之前。
不管當前記憶體是否足夠,都會被垃圾收集器回收掉。虛引用? ? ?又稱為幽靈引用或者幻影引用。
一個對象是否有虛引用的存在。全然不會對其存留時間構成影響,也無法通過一個虛引用來取得一個對象的執行個體。
為一個對象設定虛引用的唯一目的就是能在這個對象被收集器回收時收到一個系統通知。
生存還是死亡在可達性分析演算法中。即使不可達的對象。也不一定是死亡的。要真正確定一個對象的死亡,至少要經曆倆次標記的過程:第一次標記:? ? ?對象在進行可達性分析後發現沒有和GC Roots相串連的引用鏈? ? ?而且進行了一次篩選。篩選的條件是該對象有沒有必要運行finalize()方法? ? ? ? ? 當該對象沒有重寫finalize()方法,或者finalize()方法已經被虛擬機器運行過,則覺得沒有必要運行finalize()方法? ? ? ? ? 當該對象須要運行finalize()方法,則會被放進一個叫做F-Queue的隊列,由虛擬機器自己主動運行第二次標記:? ? ?finalize()方法是對象逃脫死亡的最後一次機會,GC會對F-Queue隊列進行第二次標記。? ? ?假設對象在finalize()方法中成功解救自己:? ? ? ? ? 僅僅要與引用鏈上的不論什麼一個對象建立關係就能夠。
? ? ?那麼第二次標記,它將被移除即將回收的集合。假設該對象這個階段還沒有逃脫,那麼它就被回收了。以下是代碼。原書上的內容:
package com.smile.three;/** * 此代碼示範了兩點: * 1.對象能夠在被GC時自我解救。 * 2.這樣的自救的機會僅僅有一次。由於一個對象的finalize()方法最多僅僅會被系統自己主動調用一次 * @author 深入理解JVM作者周志明zzm */public class FinalizeEscapeGC { //定義對象 public static FinalizeEscapeGC SAVE_HOOK = null; //活著的對象能夠調用的方法 public void isAlive() { System.out.println("yes, i am still alive :)"); } //重寫finalize()方法 @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("finalize mehtod executed!finalize方法被運行了"); FinalizeEscapeGC.SAVE_HOOK = this; } public static void main(String[] args) throws Throwable { //建立對象並賦值 SAVE_HOOK = new FinalizeEscapeGC(); //對象第一次成功解救自己 SAVE_HOOK = null; System.gc(); // 由於Finalizer方法優先順序非常低,暫停0.5秒,以等待它 Thread.sleep(500); if (SAVE_HOOK != null) { SAVE_HOOK.isAlive(); } else { System.out.println("no, i am dead :("); } // 以下這段代碼與上面的全然同樣,可是這次自救卻失敗了 SAVE_HOOK = null; System.gc(); // 由於Finalizer方法優先順序非常低,暫停0.5秒,以等待它 Thread.sleep(500); if (SAVE_HOOK != null) { SAVE_HOOK.isAlive(); } else { System.out.println("no, i am dead :("); } }}
注意一點:
不論什麼一個對象的finalize()方法都僅僅會被系統運行一次。
所以在第二次回收的時候。它的finalize()方法就失效了。
回收方法區方法區,在分代演算法中被稱為永久代,而在永久代進行GC要比新生代的回收"性價比"要低非常多。永久代記憶體回收的內容:? ? ?廢棄常量:? ? ? ? ? 沒有不論什麼引用,進入常量池的常量,甚至常量池中的常量也可能被回收。
? ? ?沒用的類:? ? ? ? ? 沒用的類標準:? ? ? ? ? 1、該類全部的執行個體都被回收,java堆中不存在不論什麼該類的執行個體。? ? ? ? ? 2、載入該類的ClassLoader已經被回收? ? ? ? ? 3、該類相應的java.lang.Class對象沒有在不論什麼地方被引用,無法在不論什麼地方通過反射訪問該類的方法。
GC回收涉及內容較多,我還在學習其中,這個系列部落格會持續更新。
《深入理解java虛擬機器》學習筆記四/垃圾收集器GC學習/一