寫在前面:博主是一位普普通通的19屆二本大學生,平時最大的愛好就是聽聽歌,逛逛B站。博主很喜歡的一句話
花開堪折直須折,莫待無花空折枝
:博主的理解是頭一次爲人,就應該做自己想做的事,做自己不後悔的事,做自己以後不會留有遺憾的事,做自己覺得有意義的事,不浪費這大好的青春年華。博主寫博客目的是記錄所學到的知識並方便自己複習,在記錄知識的同時獲得部分瀏覽量,得到更多人的認可,滿足小小的成就感,同時在寫博客的途中結交更多志同道合的朋友,讓自己在技術的路上並不孤單。
目錄:
1.Java和JVM簡介
Java的跨平臺性
JVM跨語言的平臺
JVM的位置、作用特點
2.Java代碼執行流程
3.棧的指令集架構和寄存器的指令集架構
4.JVM的生命週期
5.JVM內存的瑣碎概念
1.Java和JVM簡介
1.1Java的跨平臺性
我們都知道java是可以跨平臺的,其實我們字節碼文件本身就可以跨平臺的,不同操作系統有不同版本的JVM,但是對字節碼文件的識別都是一樣的
1.2JVM跨語言的平臺
其實我們Java虛擬機不是說只能解釋Java的字節碼文件,還能解釋其他語言的字節碼文件,只不過需要我們在編譯其他的語言的時候,編譯器遵循JVM的規範,那麼JVM就可以對其進行解釋也就是說Java虛擬機不關心運行在其內部的程序是何種語言編寫的而只是關心字節碼文件
1.3JVM的位置、作用特點
1.JVM的位置
2.JVM的作用和特點
2.Java代碼的執行流程
Java源代碼---->編譯器(前端編譯器)---->jvm可執行的Java字節碼(即虛擬指令)---->jvm---->jvm中解釋器----->機器可執行的二進制機器碼---->程序運行。
字節碼文件:Java源代碼經過虛擬機編譯器編譯後產生的文件(即擴展爲.class的文件),它不面向任何特定的處理器,只面向虛擬機。
我們看上圖可以發現一個java源代碼變成機器指令有兩個編譯的過程
- 前端編譯:把java源碼編譯成字節碼文件
- 後端編譯:就是JIT編譯器,把字節碼指令編譯成機器指令
3.JVM的架構模型
Java編譯器輸入的指令流基本上是一種基於棧的指令集架構,另外一種指令集架構則
是基於寄存器的指令集架構。
基於棧式架構的特點:
- 設計和實現更簡單,適用於資源受限的系統;
- 避開了寄存器的分配難題:使用零地址指令方式分配。 即是一個棧的操作,我們只需要關心棧頂
- 指令流中的指令大部分是零地址指令,其執行過程依賴於操作棧。指令集更小, [但相比於寄存器操作更多,一會兒會有解釋]編譯器容易實現
- 不需要硬件支持,可移植性更好,更好實現跨平臺。棧是一個內存層面,不跟硬件打交道
基於寄存器架構的特點:
- 典型的應用是x86的二進制指令集:比如傳統的PC以及Android的Davlik虛 擬機。
- 指令集架構則完全依賴硬件,可移植性差
- 性能優秀和執行更高效: 因爲基於cpu,比較快,對硬件耦合度較高
- 花費更少的指令去完成一項操作。
- 在大部分情況下,基於寄存器架構的指令集往往都以一地址指令、二地址指令 和三地址指令爲主,而基於棧式架構的指令集卻是以零地址指令爲主。
舉個例子:我們同樣進行2+3的操作,基於棧和寄存器的計算流程如下:
上邊是基於棧,下邊是基於寄存器的
我們可以發現完成一個指令,棧的指令集小,但指令多,寄存器指令集大,指令少
總結:
由於跨平臺性的設計,Java的指令都是根據棧來設計的。不同平臺CPU架構不同,所以不能設計爲基於寄存器的。優點是跨平臺, 指令集小,編譯器容易實現,缺點是性能下降,實現同樣的功能需要更多的指令。
4.JVM的生命週期
4.1JVM的啓動
通過引導類加載器(bootstrap class loader)創建一個初始類(initial class)來完成的,這個類是由虛擬機的具體實現指定的.
4.2JVM的執行
- 一個運行中的java虛擬機有着一個清晰的任務:執行Java程序;
- 程序開始執行的時候他才運行,程序結束時他就停止;
- 執行一個所謂的Java程序的時候,真真正正在執行的是一個叫做Java虛擬機的進程。
4.3JVM的退出
- 程序正常執行結束
- 程序異常或錯誤而異常終止
- 操作系統錯誤導致終止
- 某線程調用Runtime類或System類的exit方法,或Runtime類的halt方法,並且java安全管理器也允許這次exit或halt操作
- 除此之外,JNI規範描述了用JNI Invocation API來加載或卸載Java虛擬機時,Java虛擬機的退出情況
5.JVM內存的瑣碎概念
5.1JVM內存總覽及部分解釋
- 方法區和堆區是所有線程共享的內存區域;而java棧、本地方法棧和程序員計數器是運行時線程私有的內存區域。
- Java棧又叫做Java虛擬機棧 方法區(永久代)在jdk8中又叫做元空間(Metaspace)
- 方法區用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器(JIT編譯器,英文寫作Just-In-Time Compiler)編譯後的代碼等數據。雖然Java虛擬機規範把方法區描述爲堆的一個邏輯部分,但是它卻有一個別名叫做 Non-Heap(非堆),目的應該是與 Java 堆區分開來。
- 在JDK1.7之前運行時常量池邏輯包含字符串常量池存放在方法區, 此時hotspot虛擬機對方法區的實現爲永久代
- 在JDK1.7 字符串常量池被從方法區拿到了堆中, 這裏沒有提到運行時常量池,也就是說字符串常量池被單獨拿到堆,運行時常量池剩下的東西還在方法區, 也就是hotspot中的永久代
- 在JDK1.8之後JVM 已經將運行時常量池從方法區中移了出來,在 Java 堆(Heap)中開闢了一塊區域存放運行時常量池。同時在 jdk 1.8中移除整個永久代,取而代之的是一個叫元空間(Metaspace)的區域
5.2部分名詞的概念
- Java虛擬機棧:每個線程有一個私有的棧,隨着線程的創建而創建。棧裏面存着的是一種叫“棧幀”的東西,每個方法會創建一個棧幀,棧幀中存放了局部變量表(基本數據類型和對象引用)、操作數棧、方法出口等信息。棧的大小可以固定也可以動態擴展。當棧調用深度大於JVM所允許的範圍,會拋出StackOverflowError的錯誤,這個錯誤經常出現在遞歸調用之中,當我們的遞歸深度太大時,就會報這個錯
- 本地方法棧:這部分主要與虛擬機用到的 Native 方法相關,一般情況下, Java 應用程序員並不需要關心這部分的內容。
- PC 寄存器:PC 寄存器,也叫程序計數器。JVM支持多個線程同時運行,每個線程都有自己的程序計數器。倘若當前執行的是 JVM 的方法,則該寄存器中保存當前執行指令的地址;倘若執行的是native 方法,則PC寄存器中爲undefind。
- 堆:堆內存是 JVM 所有線程共享的部分,在虛擬機啓動的時候就已經創建。所有的對象和數組都在堆上進行分配。這部分空間可通過 GC 進行回收。當申請不到空間時會拋出 OutOfMemoryError。
- 方法區:方法區也是所有線程共享。主要用於存儲類的信息、常量池、方法數據、方法代碼等。方法區邏輯上屬於堆的一部分,但是爲了與堆進行區分,通常又叫“非堆”。 方法區內存溢出時候會報錯java.lang.OutOfMemoryError錯誤。