📖前言:
? ? ? ? 學會使用Java對于一個程序員是遠遠不夠的。Java語法的掌握只是一部分,另一部分就是需要掌握Java內部的工作原理,從編譯到運行,到底是誰在幫我們完成工作的?
? ? ? ? 接下來著重對Java虛擬機,也就是JVM有一個深刻認識,對日后完成項目的開發或是更底層的開發有很大的幫助。
? ? ? ? 接下來說的均為個人的一點小見解和觀點,希望大家多多指點!
🎈JVM:
????????JVM 是 Java Virtual Machine 的簡稱,意為 Java虛擬機。
虛擬機是指通過軟件模擬的具有完整硬件功能的、運?在?個完全隔離的環境中的完整計算機系統。
常?的虛擬機:JVM、VMwave、Virtual Box。
1 .? VMwave與VirtualBox是通過軟件模擬物理CPU的指令集,物理系統中會有很多的寄存器;2.JVM則是通過軟件模擬Java字節碼的指令集,JVM中只是主要保留了PC寄存器,其他的寄存器都進?了裁剪。
🎨JVM與JDK的關系?
? ? ? ? 知道JVM的基礎概念,可能現在就有點懵了,JVM和JDK有什么關系嗎?那么開始梳理一下與JAVA開發相關的組成部分;
?JDK:
?????? ?官方:JDK(Java Development Kit)?是Java開發工具包,包含了Java編譯器(javac)、Java程序打包工具(jar)、Java程序運行環境(JRE)、文檔生成工具(javadoc)以及其他開發工具,如調試的工具(jdb)。JDK是為Java開發人員提供的完整開發環境,包含了開發和運行Java程序所需的一切。
?????????非官方:JDK就是一個工具包,JDK是JRE的超集,JDK包含了JRE的所有開發,調試和監視應用程序等工具。當要開發Java應用程序時,需要安裝JDK.
(JDK里面的工具非常多,是這么多工具才能支撐起我們編寫Java程序)
? ? ? ? 當然有一個重要的組成部分——JRE(Java程序運行環境).
接下來就針對JRE做一個詳細說明:
?JRE:
? ? ? ? 官方:JRE(Java Runtime Environment)?是Java運行環境,包含了JVM和Java類庫。JRE是運行Java程序所需的環境,它提供了Java程序運行所需的庫文件和JVM。因此,如果你只需要運行Java程序,那么只需要安裝JRE即可。? ? ? ??
? ? ? ??
需要區分的地方:
????????這里再穿插一下IDEA是什么,IDEA是編寫代碼的平臺,里面集成了一些好用的工具,例如預編譯工具,能夠在出現一些語法錯誤的時候提示我們,但是起到編譯運行的還是JVM虛擬機!
?JVM內存模型:????????
? ? ? ? 首先在開頭處需要聲明一下: JVM內存模型與JMM內存模型不是一回事,JVM內存模型就是虛擬機內部的內存模型,但是JMM內存模型主是描述Java線程之間如如何工作的,如何做到線程之間共享變量的。
? ? ? ? JVM內存模型分為5個區,由這5個區合力搭建出JVM內存模型的。
程序計數器:記錄了要執行的下一條指令的地址
棧:
? ? ? ? 虛擬機棧:保存臨時變量、參數、函數調用的關系
? ? ? ? 本地方法棧:與虛擬機棧類似、底層是C或C++代碼
堆:保存了new的對象
方法區(元數據區):保存了靜態變量、常量、常量池等
? ? ? ? 棧區、程序計數器是線程私有的
? ? ? ? 堆區、方法區是線程共享的?
🏉類加載過程:
? ? ? ? 了解了JVM內存模型,接下來需要了解.class文件在JVM讀取與加載的過程。
? ? ? ? 1.加載:讀取.class文件中的內容
? ? ? ? 2.驗證:校驗讀取到的內容是否符合JVM規范。
? ? ? ? 3.準備:為類的靜態變量分配內存,并將其初始化為默認值。
? ? ? ? 4.解析:將符號引用全部替換為直接引用
? ? ? ? 5.初始化:此階段是執行類構造器()方法的過程。此方法由編譯器自動收集類中的所有類變量的賦值動作和靜態語句塊(static{}塊)中的語句合并而成。當初始化一個類的時候,如果發現其父類還未進行初始化,則需要先觸發其父類的初始化。Java虛擬機會保證一個類的()方法在多線程下被同步加鎖。
?雙親委派模型:
? ? ? ? 在類加載時,需要遵循雙親委派模型:
? ? ? ? 如果一個類被請求加載時,首先不會自己加載,將加載的請求拋給父類,如果父類可以加載,自己就不再加載,如果父類不能加載,自己再進行加載。
優先級由高到低:標準庫類加載器->擴展庫類加載器->第三方庫類加載器->自定義類加載器
? ? ? ? 如下圖:
雙親委派模型作用:? ? ? ?
1. 避免重復加載類: ?如 A 類和 B 類都有?個?類 C 類,那么當 A 啟動時就會將 C 類加載起來,那么在 B 類進?加載時就不需要在重復加載 C 類了。2. 安全性 :使?雙親委派模型也可以保證了 Java 的核? API 不被篡改,如果沒有使?雙親委派模型,?是每個類加載器加載??的話就會出現?些問題,?如我們編寫?個稱為 java.lang.Object類的話,那么程序運?的時候,系統就會出現多個不同的 Object 類,?有些 Object 類?是用戶自己提供的因此安全性就不能得到保證了。
🥎類初始化順序(帶有繼承關系):
? ? ? ? 父類靜態代碼塊、靜態變量->子類靜態代碼塊、靜態變量->父類普通變量->父類構造函數->子類普通方法->子類構造方法
🏀GC垃圾回收:
? ? ? ? 首先需要搞清楚,哪里的垃圾需要回收,那里的垃圾不需要回收?????????
? ? ? ? 線程私有的區域中的垃圾我們不需要回收,當線程結束時,這些區域也會跟著清空
? ? ? ? 線程共享的區域,如果線程結束,但是線程共享區中的引用對象不會隨之消失。
? ? ? ? 因此著重關注的是兩個區域——堆區、方法區。
? ? ? ? 但是相較于創建的量和空間的消耗來說,堆區是垃圾回收的重災區,但是方法區也有垃圾回收機制,但是不怎么關注。
🏐如何判斷該對象是否是垃圾?
? ? ? ? 如何判定堆區中的對象就是垃圾呢?有以下幾種方法:
🍿1.引用計數法:
? ? ? ? 采用計數的方式,如何一個對象被引用,此時計數器+1,如何不在引用該對象計數器的值就-1。
造成的問題:
? ? ? ? 如果采用方法進行垃圾的判定,會造成"循環引用"的問題,例如:
class Student{private String name;public Student student;public Student(String name) {name = this.name;}
}
public class Test {public void circulation(){Student a = new Student("a");Student b = new Student("b");a.student = b;b.student = a;a = null;b = null;}
}
此時明明a、b兩個引用已經斷開指向a、b的對象,但是其內部還有相互的指向,沒有辦法被釋放了。
🍕2.可達性分析:
? ? ? ? 為了解決上述出現的問題,可以定期進行掃描,如果發現雖然被引用著,但是到達不了GCroots也會被視為垃圾,如下圖:
????????? ? ? ? ? ? ?
?????????也就是從GC Roots開始掃描,如果是能到達的對象此時構成了引用鏈,如果在某個對象在經過下次掃描不在這條引用鏈上了,就被視為"垃圾"。
? ?該方法被作為GC垃圾回收的首選方法。但是在這個過程中可能會造成STW(Stop The World),可以理解為時間停頓。
🥯垃圾回收方法:
? ? ? ? 通過可達性分析以后判斷出哪些對象是垃圾,接下來就來談一談這些垃圾是如何被回收的?
有以下方法:
🌭?1.標記回收:
? ? ? ? 經過可達性分析以后,如果是垃圾,此時被標記一下。之后經過多輪檢測之后,將標記的垃圾進行清除:
????????
缺點:
? ? ? ? 造成內存碎片化問題,此時在存儲效率上會大減折扣。
🧇2.標記整理:
? ? ? ? 在標記回收算法的基礎上,進行改良,因為上述的做法會造成內存碎片問題,如果每次回收以后,對內存中存活的對象繼續寧整理,整理在一起,此時內存空間可利用率會提高。
????????
缺點:
? ? ? ? 整理復制需要消耗大量的實踐,時間效率降低。
🧈3.復制算法:
? ? ? ? 如果我么每次將內存空間分為兩部分,一部分空出來,另一部分使用,垃圾回收只需要對使用的一部分進行回收,進行標記,經過多輪之后,將存活的對象復制到另一塊沒有使用的內存當中。
?
缺點:
? ? ? ? 如果存活的對象較多,此時復制時比較消耗時間。
🍞4.分代回收算法:
? ? ? ? 將堆區中的區域劃分為新生代與老年代。
?
新生代又被劃分為:伊甸區、幸存區。
- 堆內存為兩個區:新生代 (Young) 和老年代 (Old);
- 新生代默認占堆內存的 1/3,老年代默認占堆內存的 2/3;
- 新生代又分為 Eden 區、Survivor From區、Survivor To區默認比例是 8:1:1? ?
過程:
? ? ? ? 首次被創建的對象先進入伊甸區,之后經過可達性分析與復制算法,將幸存的對象放入幸存區,再次經多輪過可達性分析與復制算法,此時將最終存活的對象放入老年代。老年代中進行可達性分析的頻次就比較低了。
😶常見的GC垃圾回收器:
? ? ? ? 上述講的都是一些垃圾回收的一些思路,這些GC垃圾回收器內部回收垃圾時都有用到上述的思路。
1??1.GMS垃圾收集器:
? ? ? ? 老年代收集器,并發GC,GMS是一種獲取最短停頓時間(STW)為目標的收集器。
實現原理:
? ? ? ? 其內部是基于標記-清除算法實現的。(具體大家可以自行了解)
由于整個過程中耗時最?的并發標記和并發清除過程收集器線程都可以與??線程?起?作,所以,從總體上來說,CMS收集器的內存回收過程是與??線程?起并發執?的。? ? ? ?
2??2.G1垃圾收集器 :
? ? ? ? 全區域的垃圾收集器,其主要的做法是將分代回收算法中的區域劃分為更小的區域塊,此時不再是一次性進行復制算法回收,只對一部分進行可達性分析+復制算法回收。


初始標記 (Initial Mark)階段 - G1需要暫停應?程序的執?,它會標記從根對象出發,在根對象的第?層孩?節點中標記所有可達的對象。但是G1的垃圾收集器的Initial Mark階段是跟minor gc?同發?的。也就是說,在G1中,你不?像在CMS那樣,單獨暫停應?程序的執?來運?Initial Mark階段,而是在G1觸發minor gc的時候?并將年?代上的Initial Mark給做了。并發標記 (Concurrent Mark)階段 - 在這個階段G1做的事情跟CMS?樣。但G1同時還多做了?件事 情,就是如果在Concurrent Mark階段中,發現哪些Tenured region中對象的存活率很?或者基本沒有對象存活,那么G1就會在這個階段將其回收掉,?不?等到后?的clean up階段。這也是Garbage First名字的由來。同時,在該階段,G1會計算每個 region的對象存活率,?便后?的clean up階段使? 。最終標記 (CMS中的Remark階段) - 在這個階段G1做的事情跟CMS?樣, 但是采?的算法不同,G1采??種叫做SATB(snapshot-at-the-begining)的算法能夠在Remark階段更快的標記可達對象。篩選回收( Clean up/Copy)階段 - 在G1中,沒有CMS中對應的Sweep階段。相反 它有?個Cleanup/Copy階段,在這個階段中,G1會挑選出那些對象存活率低的region進?回收,這個階段也是和minor gc?同發?的。