## 虛擬機及其定義
### 虛擬機概述
**虛擬機**: 模擬某種計算機體系結構,執行特定指令的軟件;虛擬機一般分為 系統虛擬機、進程虛擬機。
系統虛擬機:如 Virtual Box、VMware 等,完整的模擬整個操作系統。
進程虛擬機:如 JVM、Adobe Flash Player、FC模擬器 等,進程虛擬機不會完整的模擬系統,而只是模擬某個特定指令級的虛擬機。
高級語言虛擬機:如 JVM、.NET CLR、P-Code 等,高級語言虛擬機屬于進程虛擬機的一種,只不過更加細致化,將模擬某個指令級限定為某個高級語言。
Java 語言虛擬機:將高級語言限定為 Java 語言,但是并不一定所有的 Java 虛擬機都能稱之為 JVM;
**JVM**:
必須通過 Java TCK (Technology Compatibility Kit) 的兼容性測試才能稱之為 JVM。
JVM 并非一定執行 “Java” 程序,JVM 面對的是 class 字節碼;比如 Scala 語言,其生成也是 class ,便可運行在 JVM 上。
業界三大商用虛擬機:Oracle HotSpot、Oracle JRockit VM、IBM J9 VM
Java 虛擬機概念遵循 “公有設計,私有實現”,即設計規范必須遵循,但是具體實現可以不同,只要在外表上一致即可,比如 JVM 規范要求內存可以自動釋放,但每個虛擬機所采用的 GC 算法實現可能不盡相同;但最終要保證和達到的目的就是:同一份代碼不做任何修改,在不同 JVM 上都能正確運行。
### Java虛擬機運行時數據區
JVM 數據區定義:
在 JVM 規范中定義了若干種程序運行時需要的存儲不同數據類型的數據區域;某些區域是全局共享的,隨著虛擬機啟動而創建,隨著虛擬機關閉而銷毀;某系區域是線程私有的,隨著線程啟動而創建,隨著線程停止而銷毀。所有的 Java 虛擬機都遵從該規范。
JVM 數據區劃分:
Java 虛擬機數據區主要分為5大區塊:程序計數器、Java 堆、Java 虛擬機棧、本地方法棧、方法區;
圖例如下:

從上圖可以看出,方法區和堆是所有線程共享的,虛擬機棧、本地方法棧、程序計數器是每個線程私有的。
程序計數器:
程序計數器是一塊非常小的內存空間,其作用是記錄當前線程執行字節碼的行號。如果當前線程執行的是一個 Java 方法,則該區域記錄的是 class 地址,如果執行的是一個 本地的 Native 方法,則此區域為空。此區域是唯一一個在 JVM 規范中沒有規定內存溢出等內存異常的區域。
## Java 虛擬機棧和本地方法棧
### Java 虛擬機棧的特征
* 線程私有:Java虛擬機棧是線程私有的,其生命周期與線程相同;Java 虛擬機棧描述的是 Java 方法執行時的內存模型;每個方法在執行時都會創建一個棧幀,用來保存這個方法的操作數棧、局部變量表、方法出口、動態鏈接等信息;每個方法運行的過程就對應了一個棧幀在 Java 虛擬機棧中入棧和出棧的過程。
* 后進先出(LIFO)棧:Java 虛擬機棧是一個后進先出的棧,后面進入的棧幀會優先出棧;
* 存儲棧幀:每個 Java 方法的調用、執行和退出,都和存儲的棧幀有著密切的聯系。
* 異常:JVM 規范對此定義了兩種異常狀況:OutOfMemoryError、StackOverflowError;如果線程請求的棧深度大于 JVM 允許的棧深度,就會拋出 StackOverflowError異常。
### 本地方法棧的特征
* 線程私有:同上
* 后進先出(LIFO)棧:同上
* 作用是支撐 Native 的調用、執行和退出
* 異常:同上
本地方法棧實現各不相同,如 Oracle HotSport 直接將 Java 虛擬機棧和 本地方法棧合二為一。
### 棧幀的概念和特征
* 棧幀被設計為用于存儲數據和部分過程結果的數據結構,同時也被用于存儲動態鏈接、方法返回值和異常分派。
* 每一個完整的棧幀都包含:局部變量表、操作數棧、動態鏈接信息、方法正常完成和異常完成信息;在程序運行時,棧幀需要多大的局部變量表、多深的操作數棧在編譯期就已經確定了;Java 編譯器會將其寫入 class 字節碼的 code 表中。
* 在程序執行時,方法調用鏈可能會很長,很多方法都處于執行狀態,對于執行引擎而言,在活動線程中只有位于 Java 虛擬機棧棧頂的棧幀才是有效的;該棧幀被稱為當前棧幀,與之對應的關聯方法被稱之為當前方法,虛擬機執行中所有執行的字節碼指令都針對當前棧幀和當前方法進行操作。
關于局部變量表和操作數棧大體內存圖如下:

### 局部變量表
定義:局部變量表是一組局部變量值的存儲空間,用于存儲方法參數、方法內部定義的變量等;
在 JVM 編譯時,就已經在 class code表中確定了該方法所需要局部變量表的最大容量;局部變量表以變量槽(Slot)為最小單位,JVM 規范并未明確說明一個 Slot 所占用的空間大小。只是非常有導向性的描述了一個 Slot 應該能存放一個布爾型、字節型、字符型、短整型、整型、浮點型、引用型( reference)、返回地址(returnAddress)型的數據(兩個 Slot 可以存放 long 或 double 型的數據);在這8中數據類型中,他們都可以使用32位甚至更小的內存空間來存儲;這種規定允許 Slot 的大小隨著不同 JVM 實現、不同操作系統、甚至是不同硬件如 CPU 等而變化,但是必須保證每個 JVM 上棧幀的 Slot 至少在外觀上保持一致。
* reference:即對象實例引用類型,JVM 規范并未完全說明該類型應該是何種數據結構甚至占用空間大小等,但此類型至少能完成2件事:1、通過該 reference 至少能直接或者間接的找到該引用引用的對象在 Java 堆中實例的具體內存地址;2、通過該 reference 能直接或者間接的找到這個對象所屬的數據類型在 Java 方法區中的類型信息。
* returnAddress:即返回地址型,該種類型目前已經不被使用,以前主要是為了字節碼的3條指令:jsr、jsrw、ret服務的;他指向了一條字節碼指令的地址,在以前的 JVM 中使用這幾條命令和 returnAddress 實現異常處理。
* long & double:同 JMM 規范類似,在 Slot 中同樣允許對64位的 long、double類型分成2個32位的操作進行,不同的是由于局部變量表建立在 Java 虛擬機棧之上,屬于線程私有,所以不會產生原子性的線程安全問題,同時 JVM 也不允許通過 class 直接訪問 Slot 中的 long 和 double 類型。
* JVM 通過索引值方式定位局部變量表中的數據,索引值從0開始,到局部變量表最大范圍結束,對于32位的類型數據,索引n就代表訪問第n個Slot中的數據,對于64位的類型數據,索引n代表第n和n+1個Slot中的數據。對于64位的數據,JVM不允許通過任何方式單獨訪問其中一個 Slot 數據。
局部變量表用于方法間參數傳遞,以及方法執行過程中存儲基礎數據類型和對象引用;如果正在執行的方法是實例方法(new 的對象里的),那么該方法的局部變量表第0個Slot將存儲該方法所對應的實例引用;可以通過關鍵字 bis(不一定對) 來訪問這個隱含參數,其他變量按順序存儲在局部變量表中;為節省空間,Slot可以被重用,如果方法體中定義的變量作用域,如果當前程序計數器范圍查過了其作用域,那么該變量所占用的局部變量表中的 Slot 將被釋放并重用。
### 操作數棧
定義:操作數棧也稱操作棧,與局部變量表相同,其大小在編譯期確定。
操作數棧由若干個 Entry 組成,JVM 規范定義,在操作數棧中,任意一個棧元素(Entry)都可以存儲任意數據類型的數據,包括64位的 long 和 double;但普通32位的數據類型 Entry 深度為1,long 和 double Entry 深度為2;
在方法執行過程中,操作數棧棧幀用于存儲計算參數和計算結果;在方法調用時,操作數棧也用來準備調用方法的參數以及接收返回值;在方法剛開始執行時,操作數棧是空的,在執行過程中,各種不同的字節碼指令向操作數棧中寫入和讀取數據。比如一個int型加法操作,JVM必須保證操作數棧棧最接近棧頂的兩個元素都是int型,相加的過程對應兩個元素出棧相加并入棧的過程。出棧的數據類型必須與字節碼的類型嚴格匹配,在編譯時,編譯器會嚴格檢查該項。
在 Java 虛擬機棧中,往往兩個棧幀是相互獨立的,但在 JVM 實現中,往往對其進行優化,比如讓兩個邏輯上獨立的棧幀出現一部分重疊,讓下面棧幀的操作數棧和上面棧幀的局部變量表重疊,這樣在方法互相調用參數傳遞時他們就共用一部分數據,從而避免了數據復制帶來的性能損失。
局部變量表類似于方法執行時的永久存儲區,而操作數棧類似于temp緩存區,所有運算借助操作數棧完成,最終結果返回到方法局部變量表。
### 異常情況
* 如果線程請求的棧容量超出 Java 虛擬機棧容量,那么將拋出 StackOverflowError 異常。
* 如果 Java 虛擬機棧可以動態擴展,但嘗試擴展的動作無法申請到足夠的內存去完成擴展,或者在建立新線程時無法獲得足夠的內存去創建對應的虛擬機棧,那么將拋出 OutOfMemoryError 異常。
* JVM 參數:-Xss用于控制 Java 虛擬機棧的棧深度,注意:Oracle HotSpot 虛擬機 Java 虛擬機棧和本地方法棧是一個,所以此參數將控制兩個棧的總體棧深度。
- java
- 設計模式
- 設計模式總覽
- 設計原則
- 工廠方法模式
- 抽象工廠模式
- 單例模式
- 建造者模式
- 原型模式
- 適配器模式
- 裝飾者模式
- 代理模式
- 外觀模式
- 橋接模式
- 組合模式
- 享元模式
- 策略模式
- 模板方法模式
- 觀察者模式
- 迭代子模式
- 責任鏈模式
- 命令模式
- 備忘錄模式
- 狀態模式
- 訪問者模式
- 中介者模式
- 解釋器模式
- 附錄
- JVM相關
- JVM內存結構
- Java虛擬機的內存組成以及堆內存介紹
- Java堆和棧
- 附錄-數據結構的堆棧和內存分配的堆區棧區的區別
- Java內存之Java 堆
- Java內存之虛擬機和內存區域概述
- Java 內存之方法區和運行時常量池
- Java 內存之直接內存(堆外內存)
- JAVA內存模型
- Java內存模型介紹
- 內存模型如何解決緩存一致性問題
- 深入理解Java內存模型——基礎
- 深入理解Java內存模型——重排序
- 深入理解Java內存模型——順序一致性
- 深入理解Java內存模型——volatile
- 深入理解Java內存模型——鎖
- 深入理解Java內存模型——final
- 深入理解Java內存模型——總結
- 內存可見性
- JAVA對象模型
- JVM內存結構 VS Java內存模型 VS Java對象模型
- Java的對象模型
- Java的對象頭
- HotSpot虛擬機
- HotSpot虛擬機對象探秘
- 深入分析Java的編譯原理
- Java虛擬機的鎖優化技術
- 對象和數組并不是都在堆上分配內存的
- 垃圾回收
- JVM內存管理及垃圾回收
- JVM 垃圾回收器工作原理及使用實例介紹
- JVM內存回收理論與實現(對象存活的判定)
- JVM參數及調優
- CMS GC日志分析
- JVM實用參數(一)JVM類型以及編譯器模式
- JVM實用參數(二)參數分類和即時(JIT)編譯器診斷
- JVM實用參數(三)打印所有XX參數及值
- JVM實用參數(四)內存調優
- JVM實用參數(五)新生代垃圾回收
- JVM實用參數(六) 吞吐量收集器
- JVM實用參數(七)CMS收集器
- JVM實用參數(八)GC日志
- Java性能調優原則
- JVM 優化經驗總結
- 面試題整理
- 面試題1
- java日志規約
- Spring安全
- OAtuth2.0簡介
- Spring Session 簡介(一)
- Spring Session 簡介(二)
- Spring Session 簡介(三)
- Spring Security 簡介(一)
- Spring Security 簡介(二)
- Spring Security 簡介(三)
- Spring Security 簡介(四)
- Spring Security 簡介(五)
- Spring Security Oauth2 (一)
- Spring Security Oauth2 (二)
- Spring Security Oauth2 (三)
- SpringBoot
- Shiro
- Shiro和Spring Security對比
- Shiro簡介
- Session、Cookie和Cache
- Web Socket
- Spring WebFlux