==============java
讀書筆記系列
==============算法
接下來的幾篇筆記主要介紹一下咱們最常談論的垃圾回收以及內存分配策略。Java 技術體系中所提倡的自動內存管理最終能夠概括爲自動化地解決了兩個問題:給對象分配內存以及回收分配給對象的內存。既然是「自動化」,那爲何還要去了解呢?答案很簡單:當須要排查各類內存溢出、內存泄露問題時,當垃圾收集成爲系統達到更高併發量的瓶頸時,咱們就須要對這些「自動化」的技術實施必要的監控和調節。本篇咱們先從如何判斷 Java 對象已死開始。緩存
圖1. 如何判斷對象已死併發
圖2. JDK 1.2 以後引用的分類高併發
在堆裏面存放着 Java 世界中幾乎全部的對象實例,垃圾回收器在堆進行回收前,第一件事就是肯定這些對象的「存活」狀態。咱們稱不可能再被任何途徑使用的對象爲「死去」的對象。this
給對象添加一個引用計數器,每當有一個對象引用它時,計數器值就加 1;當引用失效時,計數器值就減 1;任什麼時候刻計數器爲 0 的對象就是不可能再被使用的。線程
客觀地說,引用計數算法(Reference Counting)的實現簡單,斷定效率也很高。可是,至少主流的 Java 虛擬機裏面沒有選用引用計數算法來管理內存,其中最主要的緣由是它很難解決對象之間相互循環引用的問題。3d
好比:對象 objA
和對象 objB
都有字段 instance
,賦值令 objA.instance = objB
及 objB.instance = objA
,除此以外,這兩個對象再無任何引用,實際上這兩個對象已經不可能再被訪問,可是它們由於互相引用着對方,致使它們的引用計數都不爲 0,因而引用計數算法沒法通知 GC
收集器回收它們。code
經過一系列的稱爲「GC Roots」 的對象做爲起始點,從這些節點開始向下搜索,搜索所走過的路徑稱爲引用鏈(Reference Chain),當一個對象到 GC Roots 沒有任何引用鏈相連(用圖論的話來講,就是從 GC Roots 到這個對象不可達)時,則證實此對象是不可用的。cdn
可做爲 GC Roots 的對象包括如下幾種:
JNI
(即通常說的 Native
方法)引用的對象不管是經過引用計數算法判斷對象的引用數量,仍是經過可達性分析算法判斷對象的引用鏈是否可達,斷定對象是否存活都與「引用」有關。在 JDK 1.2 前:若是 reference
類型的數據中存儲的數值表明的是另一塊內存的起始地址,就稱這塊內存表明着一個引用。因此此時對象只有被引用和沒被引用兩個狀態。咱們但願描述這樣一類對象:當內存空間還足夠時,則能保留在內存之中;若是內存空間在進行垃圾收集後仍是很是緊張,則能夠拋棄這些對象。不少系統的緩存功能都符合這樣的應用場景。
在 JDK 1.2 後,Java 對引用的概念進行了擴充,將引用分爲強引用(Strong Reference)、軟引用(Soft Reference)、弱引用(Weak Reference)、虛引用(Phantom Reference)4 種。
即便在可達性分析算法中不可達的對象,也並不是是「非死不可」的,這時候他們暫時處於「緩刑」階段,要真正宣告一個對象死亡,至少要經歷兩次標記過程:若是對象在進行可達性分析後發現沒有與 GC Roots 相鏈接的引用鏈,那它將會被第一次標記而且進行一次篩選,篩選的條件是此對象是否有必要執行 finalize()
方法。當對象沒有覆蓋 finalize()
方法,或者 finalize()
方法已經被虛擬機調用過,虛擬機將這兩種狀況都視爲「沒有必要執行」。
若是這個對象被斷定爲有必要執行 finazlize()
方法,那麼這個對象將會放置在一個叫作 F-Queue
的隊列之中,並在稍後由一個虛擬機自動創建的、低優先級的 Finalizer
線程去執行它。這裏所謂的「執行」是指虛擬機會觸發這個方法,但並不承諾會等待它運行結束,這樣作的緣由是,若是一個對象在 finalize()
方法中執行緩慢,或者發生了死循環(更極端的狀況),將極可能會致使 F-Queue
隊列中其餘對象永久處於等待,設置致使整個內存回收系統崩潰。finalize()
方法是對象逃脫死亡命運的最後一次機會,稍後 GC 會對 F-Queue
中的對象進行第二次小規模的標記,若是對象要在 finalize()
中成功拯救本身——只要從新與引用鏈上的任何一個對象創建關聯便可,譬如把本身(this
關鍵字)賦值給某個變量或者對象的成員變量,那麼在第二次標記時它將被移除出「即將回收」的集合;若是對象這時候尚未逃脫,那基本上它就真的被回收了。
注:任何一個對象的 finalize()
方法都只會被系統自動調用一次,若是對象面臨下一次回收,它的 finalize()
方法不會再次被執行。
方法區(永久代)的垃圾收集主要回收兩部份內容:廢棄常量和無用的類。
廢棄常量:沒有任何地方引用的常量值或者字面值。
「無用的類」斷定條件:
ClassLoader
已被回收。java.class.Class
對象沒有在任何地方被引用,沒法再任何地方經過反射訪問該類的方法。如何判斷一個對象已死呢?就是沒有被任何對象有效引用的狀況,即單獨的對象即可覺得「死亡」狀態。借用《海賊王·狂熱行動》中的一句臺詞:在 Java 世界中,一個對象,是註定沒法存活的!(原臺詞:在大海上,一我的,是註定沒法生存的!)