jvm總結

西索發表於2019-06-06

jvm總結

1. JVM的記憶體區域

JVM將記憶體分為五塊區域分別是程式計數器、虛擬機器棧、本地方法棧、堆和方法區

1.1 程式計數器

定義:

唯一一個不會發生記憶體溢位異常的記憶體區域.
儲存的是當前JVM直譯器執行命令的行號
最小的一塊區域

1.2. 虛擬機器棧

定義: 儲存的是區域性變數表等資訊

區域性變數表

  • 區域性變數表

    • reference
    • 基本型別變數
    • returnAddress

1.3. 本地方法棧

和虛擬機器棧差不多,儲存的是本地方法的資訊,在hotspot虛擬機器中將虛擬機器棧和本地方法棧合二為一

1.4. 堆

定義:
最大的一塊記憶體區域.
存放的是物件的例項資料
JVM啟動時建立
分為新生代、老年代

  • 新生代
  • 老年代
  • 元空間

    和永久代差不多,將class檔案的後設資料儲存到元空間,元空間使用的是本地記憶體

1.5. 方法區

  • 執行時常量池

    定義: 存放的是class檔案的常量池的資料

  • 常量
  • 靜態變數
  • JIT及時編譯後的程式碼
  • 類資訊

2. 垃圾收集演算法&垃圾收集器

介紹常用的垃圾收集演算法和垃圾收集器

2.1 判斷物件存活的演算法

  • 引用計數

    定義: 只作為了解,沒有虛擬機器使用這種方式.

    有物件引用就+1,物件不引用就-1,計數器為0就回收物件.

    缺點: 無法解決物件迴圈引用問題

  • 可達性分析

    定義: 只要GCRoots連線物件的例項,物件就不能被回收

    • 虛擬機器棧引用的物件
    • 方法區靜態屬性引用的物件
    • 方法區常量引用的物件
    • 本地方法區引用的 物件

2.2 垃圾收集演算法

  • 標記清除

    定義: 用在老年代,收集的空間是不連續的,大物件無法分配時,可能會提前觸發一次full GC.

  • 複製

    定義: 用在新生代,有一個Eden區和兩個survivor區, 按8:1的比例,每次minor gc的時候會把Eden和一個survivor區的存活物件分配到另一個survivor區中,如果survivor區的空間不夠,就直接分配到老年代

  • 標記整理

    定義: 和標記清除相比,就是整理的空間是連續的

  • 分代收集

    定義: 新生代用複製演算法,老年代用標記清除或標記整理演算法

2.3 垃圾收集器

  • 新生代

    • serial

      1. 單執行緒
      2. 進行垃圾收集,必須暫停其他所有工作執行緒,"Stop The World"
      3. 簡單高效,Client模式下首選
    • parNew

      1.除了使用多條執行緒進行垃圾收集之外,其他與Serial相同
      1. 可以與CMS配合
    • parallel scavenger

      1. 吞吐量收集器
      2. 最大停頓時間引數(調低停頓時間,會增加停頓頻率)
      3. 停頓比率引數(0-100)
      4. GC自適應的調節策略
  • 老年代

    • serial old

      1. 在CMS併發收集發生Concurrent Mode Failure時,作為備胎使用
    • cms

      • 停頓段,併發收集

      • 對CPU資源敏感

        CPU不足4個時,影響程式的效能

      • 無法清除浮動垃圾

        浮動垃圾: 和使用者執行緒並行執行產生的新垃圾.

        這一部分垃圾無法回收,因為cms和使用者執行緒行執行,所以需要留一部分記憶體給cms使用,老年代空間使用92%以上時啟動cms,如果空間不夠會報"Concurrent mode Failure",這時候會啟動備用方案serial old進行垃圾回收.

      • 標記清除演算法

        收集的空間是不完整的

    • parallel scavenger old
      parallel scavenger的老年代版本

  • G1

    特點:

    充分利用計算機資源
    分代收集
    空間整合
    建立可預測的停頓

    通過remember set解決全域性掃描region物件互相引用問題. 在reference物件進行寫操作的時候 ,發生一個寫中斷,如果有跨region引用就通過cardTable把資訊記錄到region的remember set中,在垃圾回收的時候通過列舉根節點和remember set就可以不會遺漏要回收的垃圾物件了.

    回收的四個步驟:

    初始標記
    併發標記
    最終標記
    篩選回收

    找到所有GCRoots,修改ntmd的值,在進行併發標記的時候,保證使用者執行緒可以將物件分配在正確可用的region中
    使用者執行緒和垃圾收集執行緒同時執行
    同時執行產生變動的那部分,記錄到remember set logs 中,並把這個logs的內容放到 remember set中
    在使用者規定的時間裡,回收最大價值的region的垃圾

    • 特點

      • 充分利用計算機資源
      • 分代收集
      • 空間整合
      • 建立可預測的停頓
    • 垃圾收集步驟

      • 初始標記

        找到所有GCRoots,修改ntmd的值,在進行併發標記的時候,保證使用者執行緒可以將物件分配在正確可用的region中

      • 併發標記

        使用者執行緒和垃圾收集執行緒同時執行

      • 最終標記

        同時執行產生變動的那部分,記錄到remember set logs 中,並把這個logs的內容放到 remember set中

      • 篩選回收

        在使用者規定的時間裡,回收最大價值的region的垃圾

2.3.1 垃圾收集器配合使用圖

jvm總結

之所以進行這麼多選擇,是為了盡全力避免Full GC.

2.4 記憶體分配與回收策略

  • 物件優先在eden分配
  • 大物件直接進入老年代
  • 長期存活的物件將進入老年代
  • 動態物件年齡判定

同年齡的物件的大小超過整個Survivor區的一半,大於等於這個年齡的物件都會被放入老年代.

  • 空間分配擔保流程圖

jvm總結

3.1 實戰

解決問題

  • 堆溢位

    報錯: java.lang.OutOfMemoryError: Java heap space

    原因: 一般都是應該回收的物件沒有回收造成的記憶體溢位.

    解決: 看是否有應該回收的物件沒有回收,如果沒有嘗試擴大堆記憶體

  • 棧溢位

    報錯: StackOverflowError

    原因: 遞迴呼叫太多,一般不會出現這種錯誤

    解決: 1. 調大棧深度 2. 從程式碼入手,改正錯誤的呼叫方法

  • 方法區逸出

    Java開始使用元空間替換永久代.

    元空間不在虛擬機器中,使用的是本地記憶體.

    解決元空間導致的記憶體溢位的方法:

    先看是不是程式碼有錯誤,導致元空間佔用過多的記憶體
    如果不是,那就只能擴充套件本機記憶體了

  • 本機直接記憶體溢位

    報錯: java.lang.OutOfMemoryError: null
    at sun.misc.Unsafe.allocateMemory(Native Method) ~[na:1.8.0_201]

    解決:

    使用引數-XX:MaxDirectMemorySize=10M

    調大直接記憶體大小

  • gc日誌分析

    設定引數: -Xloggc:D:/logs/admin_client.log

    需要提前建好目錄

    • admin_client.log

設定虛擬機器引數

  • -Xmx3550m 最大堆大小
  • -Xms3550m 最小堆大小
  • -Xmn2g 新生代
  • -Xss128k 虛擬機器棧大小

    設定每個執行緒的堆疊大小。JDK5.0以後每個執行緒堆疊大小為1M,以前每個執行緒堆疊大小為256K。根據應用的執行緒所需記憶體大小進行調整。在相同實體記憶體下,減小這個值能生成更多的執行緒。但是作業系統對一個程式內的執行緒數還是有限制的,不能無限生成,經驗值在3000~5000左右。

  • 設定垃圾回收器

    在Java8中的測試,預設垃圾收集器Parallel GC是最快的,可以不進行更換

  • -XX:+HeapDumpOnOutOfMemoryError自動生成堆轉儲檔案的引數

    -XX:+HeapDumpOnOutOfMemoryError

    報錯: Failed to write core dump. Minidumps are not enabled by default on client versions

    加入引數: -XX:+CreateMinidumpOnCrash

    -Xms200m -Xmx200m -XX:+HeapDumpOnOutOfMemoryError -XX:+CreateMinidumpOnCrash

    生成的hprof檔案可以使用Jprofiler直接開啟

相關文章