先來看下百度百科的解釋:java
JVM 是 Java Virtual Machine(Java 虛擬機)的縮寫,JVM 是一種用於計算設備的規範,它是一個虛構出來的計算機,是經過在實際的計算機上仿真模擬各類計算機功能來實現的。算法
晦澀難懂有沒有,簡單理解就是說虛擬機是物理機的軟件實現。數組
Java 的設計理念是 WORA(Write Once Run Anywhere,一次編寫處處運行)。編譯器將 Java 文件編譯爲 Java .class 文件,而後將 .class 文件輸入到 JVM 中,JVM 執行類文件的加載和執行,最後轉變成機器能夠識別的機器碼進行最終的操做。多線程
每一個 Java 開發人員都知道字節碼經由 JRE(Java 運行時環境)執行。但他們或許不知道 JRE 實際上是由 Java 虛擬機(JVM)實現,JVM 分析字節碼,解釋並執行它。做爲開發人員,瞭解 JVM的 架構是很是重要的,由於它使咱們可以編寫出更高效的代碼。架構
可是 JVM 在幫咱們實現 Write Once Run Anywhere 的同時,有利有弊,由於在這個過程當中涉及到了內存管理,尤爲是多線程狀況下的內存管理問題,因此咱們更應該學習 JVM 的知識來幫助本身寫出更好的代碼。學習
根據上邊對 JVM 的概念介紹咱們知道,JVM 的主要做用在於如下兩方面,以後咱們的介紹也會以此着手。優化
軟件層面的機器碼翻譯線程
內存管理翻譯
最近也在學習《深刻理解 Java 虛擬機》這本書,此處貼個書中的圖過來:設計
下邊就詳細介紹一下這張圖中的各個組件
這個區域描述的是 Java 代碼運行時的狀態,是咱們很是關注的一個狀態-程序運行狀態,由於咱們寫代碼就是爲了運行,不運行的狀態對咱們是沒什麼吸引力的。說白了 Java 代碼不外乎 數據 指令 控制 這三類型語句,因此咱們將 JVM 運行時數據區能夠劃分爲以下兩大類:
數據
方法區
堆(Heap)
指令
虛擬機棧
本地方法棧
程序計數器
定義:指向當前線程正在執行的字節碼指令的地址 也就是行號。
注意:咱們須要思考一個問題,個人當前線程自己已經在執行了,爲何還要找個寄存器把他的執行行號記錄下來呢?
由於咱們程序執行的最小單位是線程,而線程在 CPU 上執行的時候是搶佔式的,這樣的話就存在線程被掛起的狀況,例如:有 A B 兩個線程,若是 A 線程執行過程被 B 線程搶佔了 CPU,則須要把掛起的 A 線程 當前執行到的行號存儲下來,等到 A 從新得到 CPU 時間片執行權的時候去程序計數器得到上一次執行的行號以便於繼續執行這個程序。
因此,每一個線程都有本身的 程序計數器,並且是互不干擾的,屬於線程私有區域
若是執行的是一個 Java 方法,計數器記錄的是正在執行的虛擬機字節碼指令的地址
若是執行的是一個 Native 方法,計數器的值則爲空(undefined)
定義:存儲當前線程運行方法所須要的數據、指令和返回地址,生命週期與線程相同,一樣屬於線程私有區域。
每一個 Java 方法在執行的同時都會建立一個棧幀用於存儲局部變量、操做數棧、方法出口等信息,
以下所示,這個棧幀會存儲的信息包括:
局部變量表
操做數棧
動態連接
出口
... ...
每個方法從調用直至執行完成的過程,其實真正對應的是一個棧幀在虛擬機棧中入棧到出棧的過程。
其中局部變量表存放了編譯器可知的各類基本數據類型、引用對象等。須要注意的是由於局部變量表空間長度只有 32 位,若是是 long 和 double 類型的話會佔用 2 個局部變量表空間,其餘數據類型只佔用 1 個。
注意:局部變量表所需的內存空間在編譯期間就會車隊分配完成,由於在進入一個方法時,這個方法須要在棧幀中分配多大的局部空間是徹底肯定的,方法運行期間局部變量大小是不會改變的。
和虛擬機棧相似,只不過他存儲的是當前線程調用的本地方法所須要的數據、指令和返回地址等,本地方法時標識有 Native 關鍵字的方法,此處就不展開描述了,參考上述虛擬機棧的介紹。
另外,根據《深刻理解 Java 虛擬機》這本書的介紹,有些虛擬機(如 Sun HotSpot 虛擬機)直接就把本地方法棧和虛擬機棧合二爲一了。
這塊區域屬於線程共享羣與,主要存儲的信息包括已被虛擬機你加載的類信息(類的元信息)、常量、靜態變量、JIT(編譯器編譯後的代碼)等數據。
方法區有一塊區域咱們稱之爲 運行時常量池,存放編譯期生成的各類字面量和符號引用,運行時常量池有一個重要特徵是具有動態性,也就是說在運行期間依然能夠將新的常量放入池中,咱們開發經常使用的有 String 類的 intern() 方法
屬於線程共享區域,在虛擬機啓動時建立,是虛擬機管理的內存中最大的一塊。它的惟一做用就是存放對象實例。
根據虛擬機規範的描述是:全部的對象實例及數組都要在堆上分配。固然隨着如今技術的發展優化這個也變得沒有那麼絕對,後續會進行分享。
這塊區域也是垃圾收集器管理的主要區域,現現在流行的垃圾回收器基本都採用的是分代收集算法,因此也就衍生了一些分代方式,
好比對於內存模型的劃分,在 JDK1.8 之前的版本基本是這樣的:
新生代
Eden
s0
s1
老年代
永久代
在 JDK 1.8 之後的版本:
新生代
老年代
Meta Space
此處小提一下,之因此在 JDK 1.8 之後 有了 Meta Space,其設計的目的在於規避永久代溢出的問題,由於 Meta Space 是能夠自動擴容的,就跟 Java 中的集合同樣。
以上種種的劃分方式,都是爲了更好地回收內存或者分配內存,從下一篇開始就開始學習內存分配及垃圾回收相關算法啦!
JVM 負責軟件層面的機器碼翻譯,能夠把咱們寫的 .java 文件翻譯成機器能夠識別的機器碼
JVM 負責內存管理
JVM 的運行時數據區包括方法區、堆、虛擬機棧、本地方法棧和程序計數器
JVM 中的方法區和堆區是全部線程共享的,其餘區域都是線程獨享的