記錄學習路上的所見,若有紕漏還望多多包涵。 Alice程序員
垃圾(Garbage)就是程序須要回收的對象,若是一個對象不在被直接或間接地引用,那麼這個對象就成爲了「垃圾」,它佔用的內存須要及時地釋放,不然就會引發「內存泄露」。有些語言須要程序員來手動釋放內存(回收垃圾),有些語言有垃圾回收機制(GC),例如我正在學習的Java語言,存在垃圾回收機制。算法
在學習Java GC 以前,咱們須要記住一個單詞:stop-the-world 。它會在任何一種GC算法中發生。stop-the-world 意味着JVM由於須要執行GC而中止了應用程序的執行。當stop-the-world 發生時,除GC所需的線程外,全部的線程都進入等待狀態,直到GC任務完成。GC優化不少時候就是減小stop-the-world 的發生,縮短因GC線程中斷時間,保證程序運行的效率。緩存
標記清除(Mark and Sweep)是最先開發的GC算法。併發
原理:兩次掃描,第一次從根開始將可能被引用的對象進行以遞歸的方式進行標記標記,第二次將未標記的對象進行回收,回收成功後將標記的引用對象清除標記爲下次的GC回收作準備。函數
GC時間:與存活對象數和程序對象總數有關,由於要對全部對象進行遞歸查看是否能夠進行回收。性能
思考:若是存活對象的數量少,那麼標記階段的實際標記不多,剩下的全爲不在引用的對象,那麼標記階段的效率便會下降,從而整個GC過程的效率也會下降。學習
標記清除的方式節省內存可是兩次掃描須要更多的時間,對於垃圾比例較小的狀況佔優點。優化
複製收集(Copy and Collection)會將從根開始被引用的對象複製到另外的空間中去,而後再捨棄掉舊的空間,利用新的空間。 線程
原理:複製收集的方式只須要對對象進行一次掃描。準備一個「新的空間」,從根開始,對對象進行掃,若是存在對這個對象的引用,就把它複製到「新空間中」。一次掃描結束以後,全部存在於「新空間」的對象就是全部的非垃圾對象。對象
思考:複製收集具備局部性,在複製收集的過程當中,會按照對象被引用的順序將對象複製到新空間中。因而,關係較近的對象被放在距離較近的內存空間的可能性會提升,這叫作局部性。局部性高的狀況 下,內存緩存會更有效地運做,程序的性能會提升。
複製收集更快速可是須要額外開闢一塊用來複制的內存,對垃圾比例較大的狀況佔優點。
引用計數是指,針對每個對象,保存一個對該對象的引用計數,該對象的引用增長,則相應的引用計數增長。若是對象的引用計數爲零,則回收該對象。
原理:每一個對象中保存該對象的引用計數,當引用發生增減時對計數進行更新,發生時間點:變量賦值、對象內容更新、函數結束(局部變量再也不被引用)。當一個對象的引用計數爲零時則說明它再也不被引用,所以釋放相應的內存空間。
思考:引用計數最大的優勢就是容易實現。成本小,基本上引用計數爲0的時候垃圾會被當即回收,而其餘方法難以預測對象的生命週期,垃圾存在的時間都會比這個方法長。這種垃圾回收方式產生的中斷時間最短。
但若是對象中存在循環引用,就沒法被回收。引用計數不適合在並行中使用,多個線程同時操做引用計數,會引發數值不同的問題從而致使內存錯誤。因此引用計數必須採用獨佔方式,若是引用操做頻繁,那麼加鎖等併發控制機制的開銷是至關大的。
JVM GC只回收堆區和方法區內的對象。而棧區的數據,在超出做用域後會被JVM自動釋放掉,因此其不在JVM GC的管理範圍內。
斷定對象是否可回收的標準:
- 對象沒有引用
- 做用域發生未捕獲異常
- 程序在做用域正常執行完畢
- 程序執行了System.exit()
- 程序發生意外終止(被殺線程等)
在Java程序中不能顯式的分配和註銷緩存,由於這些事情JVM都幫咱們作了,那就是GC。
有些時候咱們能夠將相關的對象設置成null 來試圖顯示的清除緩存,可是並非設置爲null 就會必定被標記爲可回收,有可能會發生逃逸。
將對象設置成null 至少沒有什麼壞處,可是使用System.gc() 便不可取了,使用System.gc() 時候並非立刻執行GC操做,而是會等待一段時間,甚至不執行,並且System.gc() 若是被執行,會觸發Full GC ,這很是影響性能。
Java的垃圾回收是分代回收的,分爲三代:新生代,老年代,持久代。
新生代(Young generation):絕大多數最新被建立的對象都會被分配到這裏,因爲大部分在建立後很快變得不可達,不少對象被建立在新生代,而後「消失」。對象從這個區域「消失」的過程咱們稱之爲:Minor GC 。
老年代(Old generation):對象沒有變得不可達,而且重新生代週期中存活了下來,在新生代的戰場上奮勇存活下來的對象,是不會輕易狗帶的,嘻嘻。會被拷貝到這裏。其區域分配的空間要比新生代多。也正因爲其相對大的空間,發生在老年代的GC次數要比新生代少得多。對象從老年代中消失的過程,稱之爲:Major GC 或者 Full GC。
持久代(Permanent generation)也稱之爲 方法區(Method area):用於保存類常量以及字符串常量。注意,這個區域不是用於存儲那些從老年代存活下來的對象,這個區域也可能發生GC。發生在這個區域的GC事件也被算爲 Major GC 。只不過在這個區域發生GC的條件很是嚴苛,必須符合如下三種條件纔會被回收:
一、全部實例被回收
二、加載該類的ClassLoader 被回收
三、Class 對象沒法經過任何途徑訪問(包括反射)
爲了更好的理解GC,咱們來學習新生代的構成,它用來保存那些第一次被建立的對象,它被分紅三個空間:
· 一個伊甸園空間(Eden)
· 兩個倖存者空間(Fron Survivor、To Survivor)
默認新生代空間的分配:Eden : Fron : To = 8 : 1 : 1
每一個空間的執行順序以下:
一、絕大多數剛剛被建立的對象會存放在伊甸園空間(Eden)。
二、在伊甸園空間執行第一次GC(Minor GC)以後,存活的對象被移動到其中一個倖存者空間(Survivor)。
三、此後,每次伊甸園空間執行GC後,存活的對象會被堆積在同一個倖存者空間。
四、當一個倖存者空間飽和,還在存活的對象會被移動到另外一個倖存者空間。而後會清空已經飽和的哪一個倖存者空間。
五、在以上步驟中重複N次(N = MaxTenuringThreshold(年齡閥值設定,默認15))依然存活的對象,就會被移動到老年代。
從上面的步驟能夠發現,兩個倖存者空間,必須有一個是保持空的。若是兩個兩個倖存者空間都有數據,或兩個空間都是空的,那必定是你的系統出現了某種錯誤。
咱們須要重點記住的是,對象在剛剛被建立以後,是保存在伊甸園空間的(Eden)。那些長期存活的對象會經由倖存者空間(Survivor)轉存到老年代空間(Old generation)。
也有例外出現,對於一些比較大的對象(須要分配一塊比較大的連續內存空間)則直接進入到老年代。通常在Survivor 空間不足的狀況下發生。
老年代空間的構成其實很簡單,它不像新生代空間那樣劃分爲幾個區域,它只有一個區域,裏面存儲的對象並不像新生代空間絕大部分都是朝聞道,夕死矣。這裏的對象幾乎都是從Survivor 空間中熬過來的,它們毫不會輕易的狗帶。所以,Full GC(Major GC)發生的次數不會有Minor GC 那麼頻繁,而且作一次Major GC 的時間比Minor GC 要更長(約10倍)。
2018.01.30 記