虛擬機-- 垃圾收集 對象 區域 時機

垃圾收集器與內存分配策略

一、 GC要做的事情

  1. 哪些內存需要回收 ?
  2. 什麼時候回收 ?
  3. 如何回收 ?

二、 什麼時候需要GC ?

  1. 內存溢出、內存泄漏等;
  2. 垃圾收集收成系統達到更高併發量的瓶頸時。

三、 正文

1 哪些內存需要回收 ?

  1. 基本不考慮

    • 部分: 程序計數器、虛擬機棧、本地方法棧
    • 原因: 在編譯期基本確定了內存大小
  2. 主要考慮

    • 部分: Java堆和方法區
    • 原因: 運行期間動態分配內存。

2 什麼時候回收 ?

  • 答: 對象已死的時候
    #### 問題來了,如何判斷對象已死 ?
  1. 引用計數算法

    • 定義: 給對象中添加一個引用計數器,每當有一個地方引用它時,計數器值加1;當引用失效時,計數器值就減1;
         任何時刻計數器爲0的對象就是不可能再被使用的。
    • 好處: 實現簡單,效率高
    • 缺陷: 很難解決對象之間互相循環引用的問題
  2. 可達性分析算法

    • 定義: 通過一系列的稱爲 GC Roots 的對象作爲起始點,從這些節點開始向下搜索,搜索所走過的路徑稱爲引用連,
         當一個對象到GC Roots沒有任何引用鏈相連時,則證明此對象是不可用的。
    • java中,GC Roots對象

      • 虛擬機棧(棧幀中的本地變量表)中引用的對象
      • 方法區中類靜態屬性引用的對象
      • 方法區中常量引用的對象
      • 本地方法棧中JNI(即Native方法)引用的對象
  3. 引用

    • 第一和第二種算法判定對象是否存活都與引用有關。
    • JDK 1.2 後,引用有了新的定義,以下順序引用由強變弱

      • 強引用(Strong Reference)

      Object obj = new Object(); 程序中類似這樣的就是強引用。

      • 軟引用(Soft Reference)

      軟引用關聯的對象,在系統將要發生內存溢出異常之前,將會把這些對象列進 回收範圍之中進行第二次回收;

      • 弱引用(Weak Reference)

      弱引用關聯的對象,只能生存到下一次垃圾收集發生之前。

      • 虛引用(Phantom Reference)

      虛引用存在的唯一意義: 在對象被回收的時候,收到一個系統通知;

    • 補充:

      • 強引用是普遍存在。
      • 軟引用和弱引用都是用來描述非必須的對象
      • 虛引用又稱爲幽靈引用或者幻影引用。
  4. 生存還是死亡 ?

    • 需要注意的是,通過可達性分析算法得到的不可達的對象,也不是非死不可的。
    • 一個對象的死亡,至少經歷兩次標記過程:

      • 1 可達性分析不可達的對象,被第一次標記並且進行一次篩選

        • 篩選的條件:此對象是否有必要執行finalize()方法,,虛擬機將以下兩種情況都視爲 沒有必要執行。

          • 1 對象沒有覆蓋finalize()方法;
          • 2 finalize()方法已經被虛擬機調用過
      • 2 篩選被判定有必要執行finalize()方法,進入 F-Queue的隊列,等待被線程執行該方法。

        • 線程: 虛擬機自動創建的、優先級低的Finzlizer線程。
        • 執行:所謂的執行是指虛擬機會觸發這個方法,但不會一直等待它運行結束。

          • 原因:如果對象在Finzlize()方法中執行緩慢 或 發生死循環;可能導致F-queue隊列中其他對象
               永遠處於等待,甚至導致整個內存回收系統崩潰。
    • finalize() 方法是對象逃脫死亡命運的最後一次機會,稍後GC將對F-Queue中的對象進行第二次小規模的標記

      • 如果對象重新與引用鏈上的任何一個對象建立關聯,(例如: 對象把自己的值 -賦值給某個類的變量或對象的成員變量),移出即將回收的集合;
      • 如果對象沒有移出回收的集合,基本上確定了被回收。
    • 任何一個對象的finalize()方法只會被系統自動調用一次。
    • finalize(): 運行代價高昂,不確定性大,無法保證各個對象的調用順序。
    • 使用 try-finally 完全可以替代 finalize()方法。
  5. 回收方法區

    • 方法區(HotSpot虛擬機中的永久代)
    • java 虛擬機規範中,不要求在方法區進行垃圾收集。 因爲收集性價比比較低。

      • 堆新生代中,一次垃圾回收可以回收 70%- 95%的空間。
      • 永久代(方法區) 收集效率遠低於此。
    • 永久代的垃圾收集分爲兩部分:

      • 廢棄常量

        • 定義: 回收廢棄常量與回收Java堆中的對象類似。
        • 判斷依據: 常量池中的字面量的回收爲例, 一個字符串”a“在常量池中,當前系統中沒有String對象是"a",也沒有其他
          地方引用這個字面量,這時候如果發生內存回收,沒有必要的話,這個“a”常量會被系統清理出常量池。
        • 常量池中的其他類(接口)、方法、字段的符號引用也與此類似。

      • 無用的類

        • 判斷依據:

          • 該類所有的實例已經被回收,換句話說就是Java堆中不存在該類的任何實例。
          • 加載該類的ClassLoader已經回收。
          • 該類對應的java.lang.class對象沒有在任務地方被引用,無法在任何地方通過反射訪問該類的方法。
        • 虛擬機可以對滿足上述三個條件的對象進行回收,不是必然的。
    • 大量使用反射、動態代理、CGLib 等ByteCode框架、動態生成JSP以及OSGj這類頻繁自定義ClassLoader的場景都需要虛擬機具備類卸載的功能,以保證永久代不會溢出。

歡迎訪問:

github中更多筆記

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章