虛擬機工作原理

  接觸過java的童鞋,你是否也有這樣的疑問我們的java程序是怎樣執行的?

  今天我們來揭開java程序執行流程的神祕面紗,首先來看一下程序執行的流程圖
這裏寫圖片描述
  
  從流程圖中我們可以大概知道程序的執行流程,首先我們寫好java程序放在工作空間
然後通過輸入 javac - className.java 把java文件編譯成class文件,此時class文件還是保存在工作空間(編譯器比如eclipse點擊編譯時會自動完成這個操作)。
當我們輸入java className執行我們的程序時,JRE的類加載器從工作空間中讀取class文件,載入到系統分配給JVM的內存區域–運行數據區(Runtime Data Areas). 然後執行引擎解釋或者編譯類文件,轉化成特定CPU的機器碼,CPU執行機器碼,至此完成整個過程。

  Java程序都運行在虛擬機上,下面我們詳細的描述一下虛擬機的工作原理(即流程圖中的紅色方框裏的流程)。先來介紹一下類加載器, 類加載器作用是將類的字節碼文件(.class)中的二進制數據讀入內存,將其放在運行時數據區的方法區內,然後在堆上創建java.lang.Class對象,封裝類在方法區內的數據結構。類加載的最終產品是位於堆中的類對象,類對象封裝了類在方法區內的數據結構,並且向JAVA程序提供了訪問方法區內數據結構的接口。

  類加載器的層次關係圖。
這裏寫圖片描述

  擴展類加載器(Extension ClassLoader):該類加載器負責加載JDK安裝目錄下的\jre\lib\ext的類,或者由java.ext.dirs系統變量指定路徑中的所有類庫,開發者也可以直接使用擴展類加載器。
  
  應用程序類加載器(AppClassLoader):負責加載用戶類路徑(Classpath)所指定的類,開發者可以直接使用該類加載器,如果應用程序中沒有定義過自己的類加載器,該類加載器爲默認的類加載器。
  用戶自定義類加載器(User ClassLoader):JVM自帶的類加載器是從本地文件系統加載標準的java class文件,而自定義的類加載器可以做到在執行非置信代碼之前,自動驗證數字簽名,動態地創建符合用戶特定需要的定製化構建類,從特定的場所(數據庫、網絡中)取得java class。
  
  通過上面的層次圖發現類加載是一種委託模式,當我們的虛擬機加載一個類時,下層的加載會委託上一級的類加載器。上一級的加載器會檢查命名空間中是有已經加載了這個類,如果加載了就直接使用這個類,如果沒有加載則繼續委託到上一級直到最頂級,如果檢查到最頂級(Bootstrap加載器)都沒有找到需要的類,那麼則會從最頂級開始加載,依次向下加載直到找到這個類(這個檢查過程就類似在公司出了問題,下級能解決就解決,不能解決就依次向上級彙報。加載過程類似與android中view的事件分發機制,依次往下傳遞)。對於某個特定的類加載器來說,一個Java類只能被載入一次,也就是說在Java虛擬機中,類的完整標識是(classLoader,package,className)。一個類可以被不同的類加載器加載。整個檢查和加載過程如下所示
這裏寫圖片描述

類加載好了以後虛擬機繼續做如下操作:
這裏寫圖片描述
  
  類加載到運行時數據區,我們繼續探討一下運行時數據區。
  運行時數據區:當運行一個JVM示例時,系統將分配給它一塊內存區域(這塊內存區域的大小可以設置的),這一內存區域由JVM自己來管理。從這一塊內存中分出一塊用來存儲一些運行數據,例如創建的對象,傳遞給方法的參數,局部變量,返回值等等。分出來的這一塊就稱爲運行數據區域。
  運行時區域主要分爲6大塊:方法區、Java堆、虛擬機棧、本地方法棧、程序計數器,運行常量池。其中方法區和Java堆一樣,是各個線程共享的內存區域,而虛擬機棧、本地方法棧、程序計數器是線程私有的內存區。運行常量池本應該屬於方法區,但是由於其重要性,JVM規範將其獨立出來說明。
這裏寫圖片描述
  程序計數器:它是一塊較小的工作空間,他可以看作是當前線程所執行的字節碼的行號指示器,字節碼解釋器工作時就是通過改變這個計數值來選取下一條需要執行的字節碼指令,分支,循環,跳轉,異常處理、線程恢復等功能都需要依賴這個計數器。每條線程都需要有一個獨立的程序計數器,各條線程之間計數器互不影響,獨立存儲,如果線程正在執行的是一個java方法,這個計數器記錄的是正在執行的虛擬機字節碼指令的地址;如果正在執行的是native方法,這個計數器值則爲空,此內存區域是唯一一個沒有任何內存溢出的區域 

  Java虛擬機棧:與程序計數器一樣,Java虛擬機棧也是線程私有的,他的生命週期與線程相同,每個方法在執行的同時都會創建一個棧幀,用於存儲局部變量,操作數,動態鏈接,方法出口等信息。每個方法被調用到執行完成就對應一個棧幀在虛擬機中從入棧道出棧。
  
  本地方法棧:與虛擬機棧的作用類似。不同點在於虛擬機棧爲執行Java方法服務,而本地方法則爲虛擬機使用native方法服務。
  
  Java堆:一般來說,Java堆是java虛擬機所管理的最大一塊內存區域,在虛擬機啓動時創建。此內存區域唯一的目的就是存放對象實例,幾乎所有的對象都是在這裏分配內存,Java虛擬機規範中是這樣描述:所有的對象實例以及數組都要在堆上分配,但是隨着逃逸分析技術的逐漸成熟,所有的對象實例以及數組都要在堆上分配也變成不是絕對的。
  
  方法區:方法區和Java堆一樣,是各個線程共享的 內存區域,他用於存儲已被虛擬機加載的類信息,常量,靜態變量,即時編譯器編譯後的代碼等數據。
  
  運行時常量池:方法區的一部分,Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項信息是常量池,用於存放編譯期生成的各種字面量和符號引用,這部分內容將在類加載後進入方法區的運行時常量池中存放。
  繼續探討 執行引擎(Execution Engine)
  執行引擎負責具體的代碼調用及執行過程。就目前而言,所有的執行引擎的基本一致:
  1. 輸入:字節碼文件
   2. 處理:字節碼解析
   3. 輸出:執行結果。
  物理機的執行引擎是由硬件實現的,和物理機的執行過程不同的是虛擬機的執行引擎由於自己實現的,這部分後續在詳細說明

發佈了48 篇原創文章 · 獲贊 35 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章