Java虛擬機器學習筆記
一、JDK、JRE和JVM
JDK(Java Development Kit) 是 Java 語言的軟體開發工具包(SDK)。在JDK的安裝目錄下有一個jre目錄,裡面有兩個資料夾bin和lib,在這裡可以認為bin裡的就是jvm,lib中則是jvm工作所需要的類庫,而jvm和 lib合起來就稱為jre。
JRE(Java Runtime Environment,Java執行環境),包含JVM標準實現及Java核心類庫。JRE是Java執行環境,並不是一個開發環境,所以沒有包含任何開發工具(如編譯器和偵錯程式)。
JVM是Java Virtual Machine(Java虛擬機器)的縮寫,JVM是一種用於計算裝置的規範,它是一個虛構出來的計算機,是通過在實際的計算機上模擬模擬各種計算機功能來實現的。
總結:JDK是整個Java的核心,包括了Java執行環境JRE、Java工具和Java基礎類庫。JRE是執行JAVA程式所必須的環境的集合,包含JVM標準實現及Java核心類庫。JVM是整個java實現跨平臺的最核心的部分,能夠執行以Java語言寫的程式。
二、為什麼有必要學習JVM
Java程式無需顯示分配和回收記憶體,由JVM自動管理記憶體的分配及回收,給開發人員降低了編寫程式的難度, 副作用是可能在不知不覺中浪費了很多記憶體, 導致JVM花費很多時間進行記憶體回收, 另外由於不清楚JVM記憶體的分配和回收機制, 有可能造成記憶體洩露. 因此JVM如何進行記憶體的分配和回收也是要關注的問題。
開發過程中經常遇到OutOfMemory、Stack Overflow此類的異常,需要了解Java記憶體分配,JVM執行時候的規則才能清楚為什麼會發生,怎麼去避免出現這樣的問題。
三、JAVA記憶體結構及垃圾收集
1)堆記憶體 分為老年代和年輕代;年輕代分為Eden區、From Survivor和To Survivor
2)方法區 是各個執行緒共享的記憶體區域,用於儲存已被虛擬機器載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼等,儲存類資訊,常量、靜態變數等資料,是執行緒共享的部分。 也存在垃圾收集如廢棄常量和無用的類。 無用的類是指:該類所有例項已被回收;載入該類的classLoader已經被回收;該類的對應的Java.lang.class物件沒有任何地方被引用 3)棧
4)程式計數器 是一塊較小的記憶體空間,它可以看做是當前執行緒所執行的位元組碼的行號指示器 5)記憶體分配規則 1、物件優先在Eden上分配 2、長期存活的物件將進入老年代 -XX:MaxTenuringThreshold此引數類增到15的時候進入老生代 3、大物件直接進入老年代 4、動態物件年齡判斷 在survivor空間中相同年齡所有物件的大小的總和大於survivor空間的一半,這些物件直接進入老生代 5、空間分配擔保
6)Java虛擬機器執行原理 1、物件的建立:類載入檢查、分配記憶體、將分配到的記憶體空間初始化為零值、init 2、物件的記憶體佈局:物件頭(儲存物件自身的執行時資料和型別指標)、例項資料(物件真正儲存的有效資訊)、對齊填充 3、物件的訪問定位:控制代碼訪問和直接訪問 控制代碼訪問最大好處:reference中儲存的是穩定的控制代碼地址,物件移動時只會改變控制代碼的例項資料指標,reference本身不需要改變 直接訪問速度更快,節省了一次指標定位的時間開銷,訪問頻繁導致指標執行成本增加
7)垃圾回收判斷物件是否已死?
1、Java引用分類
強引用
垃圾收集器永遠不會回收掉被引用的物件
軟引用
在發生記憶體溢位異常之前二次回收,如果還沒有足夠記憶體,則丟擲記憶體溢位異常
弱引用
關聯物件只能生存到下次垃圾收集之前
虛引用
僅在這個物件被回收時收到一個系統通知
2、引用計數演算法:很難解決物件之間的相互迴圈引用的問題
3、可達性分析法:GCRoots向下搜尋,當一個物件到GC Roots沒有任何引用鏈相連時,證明物件不可達。
①那些物件可以作為GCROOts?從永久代到年輕代的引用:
虛擬機器棧中引用的物件
方法區中類靜態變數屬性引用的物件
方法區中常用的物件
本地方法棧中JNI引用的物件
從永久代到年輕代的引用
②列舉根節點:過程中會出現短暫的停頓,設定一組oopMap的資料結構記錄偏移量上什麼型別的資料。 安全點:特定位置暫停執行分為搶先式中斷和主動式中斷;安全區域:對不在執行的類或者物件進行列舉
8)什麼時候進行垃圾收集?
①觸發Minor GC條件(年輕代包括Eden和Survivor區域的記憶體回收):Eden區滿
②觸發Full GC條件:System.gc()、老年代空間不足、永久代空間不足、GC擔保失敗
③Major GC(老年代的清理)
9)怎麼進行垃圾收集?(垃圾收集演算法)
標記-清除:效率不高,佔用空間,記憶體碎片過多 複製演算法:代價大:將記憶體縮小到原來的一半 標記-整理:標記後讓所有存活的物件向一端移動,然後直接清理掉邊界以外的區域 分代收集: 新生代:大批物件死去,使用複製演算法;老生代:物件存活率高,沒有額外空間進行分配擔保,就使用標記 清除和標記整理來進行回收 10)垃圾收集器
Serial收集器、parNew收集器、parallel收集器、CMS收集器
四、類載入機制
1)虛擬機器把描述類的資料從class檔案載入到記憶體,並對資料進行校驗轉換解析和初始化,最終形成可被虛擬機器直接使用的Java型別 類載入的時機 遇到new,getstatic,putstatic和invokestatic這四個位元組碼指令時 使用reflect包的方法對類進行反射呼叫的時候 當初始化一個類的時候,如果發現其父類還沒有進行初始化 虛擬機器啟動時,使用者需要指定一個執行的主類
2)類載入過程:
載入: 通過一個類的全限定名來獲取定義此類的二進位制位元組流 將這個位元組流所代表的靜態儲存結構轉化為方法區的執行時資料結構 在記憶體中生成一個代表這個類的class物件,作為方法區這個類的各種資料的訪問入口 驗證階段: 檔案格式驗證 元資料驗證 位元組碼驗證 符號引用驗證 準備階段:正式為類變數分配記憶體並設定類變數初始值 解析階段: 虛擬機器將常量池內的符號引用替換成直接引用的過程 初始化階段: Java程式主導
3)雙親委派模型
自上而下為啟動類載入器、擴充套件類載入器、應用程式類載入器、自定義類載入器 如果一個類載入器收到了類載入的請求,它首先不會自己嘗試載入這個類,而是把這個請求委派給父類載入器完成,最終請求委派給頂層啟動類載入器,父類無法完成載入,子類載入器才會嘗試載入
4)應用例項
Tomcat 部署在同一個伺服器上的兩個web應用程式所使用的Java類庫實現相互隔離 部署在同一個伺服器上的兩個web應用程式所使用的Java類庫可以共享 伺服器需要儘可能保證自身不受部署的web應用程式影響 支援jsp應用的web伺服器,大多數都需要支援hotswap功能 OSGI 靈活的類載入器架構,基於Java語言的動態模組化規範 Bundle之間的依賴關係從傳統的上層模組依賴轉化為平級模組之間的依賴,類庫可見性精確的控制; export過的package才能被外界訪問 可能實現熱插拔功能,可以停用,重新安裝某些模組,不影響整體 可能出現死鎖,互相引用導致的死鎖,額外的複雜度,記憶體洩漏;解決辦法:可以啟用引數來單執行緒序列化的方式強制進行類載入動作