1. 程式人生 > 實用技巧 >JVM 小結

JVM 小結

JVM

  • 1、JVM 的位置

    • 執行在作業系統之上的

    • java程式是排在 jre(jvm) 上的


    • 所謂的JVM 調優就是在堆裡面調,方法區就是 一個特殊的堆

  • 2、JVM 的體系結構

  • 3、類的載入器

    • 作用
      • 載入 class 檔案

      • 類載入器分為好幾個,有等級

        • 1、虛擬機器自帶的載入器
        • 2、啟動類(根)載入器
        • 3、擴充套件類載入器
        • 4、應用程式(系統類)載入器
  • 4、雙親委派機制

    • 當某個類載入器需要載入 .class 檔案時,它首先把這個任務委託給他們的上級類載入器,遞迴這個操作,如果上級的類載入器沒有載入,自己才會載入這個類

    • 作用:
      • 1、防止重複載入同一個 .class。通過委託去向上面問一問,載入過了,就不用再載入一遍。保證資料安全
      • 2、保證核心 .class 不能被篡改,通過委託方式,不會去篡改核心 .class 。即使篡改也不會去載入,即使載入也不會是同一個 .class 物件了,不同的載入器載入同一個 .class 也不是同一個 Class 物件,這樣保證了 Class 執行安全。
    • 類載入的過程和使用那個載入器
      • 1、類載入器收到類載入的請求
      • 2、將這個請求向上委託給父類載入器去完成。一直向上委託,直到啟動類載入器
      • 3、啟動類載入器檢查是否能夠載入當前這個類,能載入就結束,使用當前的類載入器,否則,丟擲異常,通知子載入器進行載入
  • 5、沙箱安全機制

  • 6、Native

    • JNI (本地方法介面) java native interface

      • // native :

        1、凡是帶了 native 關鍵字的,說明 java 的作用範圍達不到了,會去呼叫底層 C 語言的庫

        2、會進入本地方法棧,呼叫 JNI, 呼叫本地方法庫,

        ​ JNI 的作用就是擴充套件 java 的使用,融合不同的語言為 java 所用,最初是 C、C++

        所以在記憶體中開闢了一塊區域,為了登記 native 方法,

        3、呼叫其他介面其實還可以使用 Socket、WebService、http、rpc 、restful

  • 7、PC 暫存器

    • 程式計數器:Program Counter Register
    • 每個執行緒都有一個程式計數器,就是一個指標,指向方法區中的位元組碼(用來儲存儲存指向像一條指令的地址,也即將要執行的指令程式碼),在執行引擎讀取下一條指令, 是一個非常小的記憶體空間,幾乎可以忽略不計
  • 8、方法區

    • Method Area:

      • 方法區是被所有的執行緒共享,所有欄位和方法位元組碼,以及一些特殊方法,如建構函式,介面程式碼也在此定義,簡單說,所有定義的方法的資訊都儲存在該區域,此區域屬於共享區間,

        • 靜態變數、常量、類資訊(構造方法、介面定義),執行時的常量存在方法區中,但是例項變數存在堆記憶體中,和方法區無關

          說白了就是:

          ​ staic、 final、Class、常量池、、、

  • 9、棧

    • 資料結構。棧和佇列進行比較

    • 棧:先進後出,後進先出

      • 所以為什麼 main 方法最後退出?

        • 棧溢位、遞迴問題(死迴圈)、無限壓棧 、壓棧超過了棧的深度

        • 棧:
          • 何時發生棧記憶體溢位?
            • 對於一臺伺服器而言,每一個使用者請求,都會產生一個執行緒來處理這個請求,每一個執行緒對應著一個棧、棧會分配記憶體,此時如果請求過多,這時候記憶體就不夠了,就會發生記憶體溢位
          • 什麼時候會發生棧溢位 ?
          • 棧溢位是指不斷的呼叫方法,不斷的壓棧,最終超出了棧允許的棧的深度,最終超出了棧允許的棧深度,就會發生棧溢位,比如遞迴操作沒有終止,死迴圈

        • 幫助記憶:
          • 可以把記憶體你做一個大箱子,棧比作一個小箱子,棧溢位是指小箱子裝不下了,而棧記憶體溢位是大箱子在也裝不下小箱子了
        • 主管程式的執行,生命週期和執行緒同步

        • 執行緒結束,棧記憶體你就會釋放

        • 對於棧來說,沒有垃圾回收的問題

        • 棧裡面放的東西:

          • 8 大基本型別+ 物件引用 + 例項的方法
        • 棧執行原理:
        • 棧、堆、方法區的互動關係
          • 棧是類模板,堆是物件例項
    • 佇列: 先進先出 (FIFO)

  • 10、三種 JVM

    • Sun 公司 HotSpot
    • IBM J9VM
    • BEA JRockit
  • 11、堆

  • 堆的圖

- 邏輯上存在,物理空間上不一定存在
  • heap、一個 jvm 只有一個堆記憶體,對記憶體的大小是可以調節的

  • 12、新生區(伊甸園區)、老年區 、倖存區0, 倖存區1

    • 新生區是類誕生和成長的地方,甚至死亡
    • 多有的物件都是在一點園區new出來的
    • GC 垃圾回收,主要是在伊甸園區和老年區

    • 假設記憶體滿了、OOM、java.lang.OutOfMemoryError: java heap space
    • 無限字串的例子 - 其實也就是無限 new 物件:

      • VM 引數除錯
  • 13、永久區

  • 這個區域常駐記憶體的,用來存放 jdk 自身攜帶的 Class 物件,Interface 元資料,儲存的是 java 執行時的一些環境或類資訊,這個區域不存在垃圾回收,關閉虛擬機器就會釋放這個區域的記憶體

  • 什麼情況下在永久區就崩了呢?
    • 一個啟動類載入了大量的第三方 jar 包,tomcat 部署了太多的應用,或者大量動態生成的反射類,不斷的被載入,直到記憶體滿了,就出現 OOM
    • jdk1.6之前: 叫永久代,常量池是在方法區

    • jdk1.7: 永久帶,但是慢慢的退化了,去永久代,常量池在堆中

    • jdk1.8之後:無永久代,常量池在元空間

    • 在一個專案中,突然出現 OOM 故障,那麼該如何排除 - 研究為什麼出錯?

      • 能夠看到程式碼第幾行出錯:

        • 記憶體快照分析工具,MAT、Jprofiler

        • debug: 只能一行行的分析程式碼

        • MAT、Jprofiler作用

          • 分析 Dump、記憶體檔案,快速定位記憶體洩漏
          • 獲得對中的資料
          • 獲得大的物件
  • 14、對記憶體調優

  • 15、GC (自動垃圾回收機制)

    • Jvm 在進行 GC 時,並不是對以下三個區域統一回收,大部分時候,回收都是新生代,

      • 新生代
      • 辛存區( from \ to)
      • 老年區
        • GC 兩種型別: 輕GC(普通 GC), 重 GC (全域性 GC)
    • GC 的題目 :

      • 1、JVM 的記憶體模型和分割槽 - 詳細到每個區放什麼?

        • 方法區
        • 本地方法棧
        • PC 計數器
      • 2、堆裡面的分割槽有哪些?

        • 新生區(伊甸園)
        • 倖存區(from\ to),誰空誰是 to
        • 老年區
      • 3、GC 的演算法有哪些?

        • 標記清除法
        • 標記壓縮
        • 複製演算法
        • 引用計數器
      • 4、輕 GC 和 重 GC 分別在什麼時候發生?

    • 常用演算法
  • 16、JMM

    • 1、什麼是 JMM?
      • Java Memory model Java 記憶體模型
    • 2、幹嘛的?
      • 作用:快取一致性協議,用於定義資料讀寫的規則(遵守,找到這個規則)

      • JMM 定義了執行緒的主記憶體之間的抽象關係:執行緒之間的共享變數儲存在主記憶體(Main Memory)中,每個執行緒都有一個私有的本地記憶體(Local Memory)

      • CPU 中執行的執行緒從主存中拷貝共享物件 obj 到它的 CPU 快取,把物件 obj 的 count 變數改為 2,但每個變數對執行在右邊 CPU 中的執行緒不可見,因為這個更改還沒有 flush 到主存中,要解決共享物件可見性這個問題,我們可以用 java volilate 關鍵字或者加鎖

      • 解決共享物件可見性這個問題: volilate
    • 3、如何學習?

      • ​ JMM: 抽象的概念 - 理論


  • 17、總結

    • 記憶體效率:

      • 效率最高: 複製演算法 > 標記清除演算法 > 標記壓縮演算法
      • 記憶體整齊度: 複製演算法 = 標記壓縮演算法 > 標記清除演算法
      • 記憶體利用率: 標記壓縮演算法 = 標記清除演算法 > 賦值演算法
    • 思考一下, 難道沒有最優的演算法嗎?

      • 沒有,沒有最好的演算法,只有最合適的演算法 --->

      • 分代收集演算法
        • 每一代都有不同的演算法

        • 年輕代:

          • 存活率低
          • 複製演算法
        • 老年代:

          • 區域大: 存活率

          • 標記清除(記憶體碎片不是太多) + 標記壓縮混合 實現