週一夜下班,我高高興興的回到家裏面,女友蹦蹦跳跳的朝我跑過來,手裏拿着掃把和拖布。這是又要打我麼?我又作錯了什麼事情麼?我大腦在高速旋轉。這時,女友打破了沉默。程序員
Garbage Collection,簡稱GC,中文名"垃圾回收"。是和計算機內存管理有關的概念,這裏面的垃圾指的是程序不用的內存空間。
算法
說到作家務,咱們確定難免要丟棄一些東西,說的文明一點叫斷舍離,說的簡單一點就是丟垃圾。線程
在現實世界中,說到垃圾,指的就是那些不讀的書、不穿的衣服等。這種狀況下的"垃圾"指的就是"本身不用的東西"。咱們在整理家務的時候,通常是要作兩件事,找到家裏不用的垃圾,把這些垃圾丟棄,以便放一些其餘的有用的東西。3d
映射到計算機系統中也同樣,計算機的內存也是有限的,不可能把全部東西都一直存放在內存中,也須要按期釋放不用的內存空間。而這些不用的內存空間中存放的東西就是垃圾了。在程序中,垃圾回收的過程就是找到內存空間中的垃圾,而後進行垃圾回收,讓程序員可以再次利用這部分空間。code
前面咱們提到過,生活中的垃圾就是那些不用的東西。可是,『不用』這件事是如何肯定的呢?cdn
在平常作家務的時候,咱們想要肯定一個東西是否能夠丟棄的時候,咱們會有不少方法。對象
第一種,咱們在房間內找到一個感受沒什麼用的usb線的時候,咱們是這樣判斷他有沒有用的:blog
一、看家裏有沒有能夠用得上這個充電口的設備。隊列
二、看家裏有沒有能夠適配這個USB線的適配器。內存
若是有的話,那麼咱們就認爲這根線是有用的,不然,這根USB線就會被咱們標記爲垃圾。等待被丟棄。
上面這種方式,在計算機的垃圾手機算法中叫作引用計數法
,其算法過程是這樣的:給對象中增長一個引用計數器,每當有一個地方引用他時,計數器就加1,當引用失效時,計數器值就減1。當執行垃圾回收時,只須要判斷這個對象的引用計數器的數值是否是0就能夠了。若是引用計數器數值爲0,則表示能夠回收。
這是一種比較簡單的算法了,這種垃圾回收方式比較簡單。
可是,這種丟垃圾的方式有一個缺點,那就是有可能效果不明顯,就像咱們想要丟棄一個USB線的時候,發現只有一個MP3可使用他,而後,咱們就把USB線保留下來了。當咱們想要丟棄MP3的時候,發現家裏還有一根USB線能夠用到他,這樣,MP3也被保留下來了。
可是,若是這個MP3和USB線根本就沒有人想要用了呢?好比這個USB線和MP3是家裏的某個客人留下的,他表示已經不須要了呢?
這就是引用計數法的缺點,就是若是存在循環引用對象,將致使沒法回收。
固然,平常生活中,咱們判斷一個東西還有沒有用,不能僅僅看是否是有東西和他"配套",仍是要看家裏人到底還用不用獲得。
因此,比較靠譜一點的判斷一個東西是否是垃圾的時候,咱們會拿着一個東西,問一遍家裏的全部成員:這東西你還須要嗎?
若是獲得的答案都是不須要的話,那就證實這個東西能夠丟棄了。這樣就避免了MP3和USB線被誤保留的尷尬。
這種方式,就是從家庭成員出發,去判斷一個東西到底有沒有用。而不是從物品之間的相關關聯關係來判斷。
上面這種垃圾判斷的方法,在計算機中叫作可達性分析算法
,這個算法的基本思路是經過一系列的"GC Root"的對象做爲起始點,從這些節點開始向下搜索,搜索所走過的路徑成爲引用鏈,當一個對象到GC Root沒有任何引用鏈相連時,則證實此對象是不可用的。
一個物品,沒有任何家庭成員宣佈須要還要繼續使用。就像一個對象,到達全部的"GC Root"都沒有引用鏈是同樣的。
在Java語言中,能夠做爲GC Root的對象包括如下幾種:
一、虛擬機棧中引用的對象。
二、方法區中類靜態屬性引用的對象。
三、方法區中常量引用的對象。
四、本地方法棧中JNI引用的對象。
通常狀況下,咱們對於一個家裏面沒用的東西處理,不太會果斷的直接扔掉。有的時候對於一些有必定記念意義的、或者比較貴重的東西會先保留一段時間,通過幾回清理,仍是以爲沒用之後,纔會被完全扔掉。
其實,計算機的垃圾回收也是同樣的。就算一個對象,經過可達性分析算法分析後,發現其是『不可達』的,也並非非回收不可的。
通常狀況下,要宣告一個對象死亡,至少要通過兩次標記過程:
一、通過可達性分析後,一個對象並無與GC Root關聯的引用鏈,將會被第一次標記和篩選。篩選條件是此對象有沒有必要執行finalize()方法。若是對象沒有覆蓋finalize()方法,或者已經執行過了。那就認爲他能夠回收了。若是有必要執行finalize()方法,那麼將會把這個對象放置到F-Queue的隊列中,等待執行。
二、虛擬機會創建一個低優先級的Finalizer線程執行F-Queue裏面的對象的finalize()方法。若是對象在finalize()方法中能夠『拯救』本身,那麼將不會被回收,不然,他將被移入一個即將被回收的對象集合。
對象如何在finalize()中『拯救』本身呢?
最簡單的方式就是從新簡歷引用,好比把本身賦值給某個類變量或者對象的成員變量。