面試官:挺厲害的,那你說說JVM加載class文件的原理機制

在面試java工程師的時候,這道題常常被問到,故需特別注意。java

認真閱讀本文後,方可作到心中有數,面試談笑風生。程序員


1,JVM簡介(簡單瞭解)

 JVM 全稱是Java Virtual Machine ,Java 虛擬機,也就是在計算機上再虛擬一個計算機,這和咱們使用 VMWare不同,那個虛擬的東西你是能夠看到的,這個JVM 你是看不到的,它存在內存中。咱們知道計算機的基本構成是:運算器、控制器、存儲器、輸入和輸出設備,那這個JVM 也是有這成套的元素,運算器是固然是交給硬件CPU 還處理了,只是爲了適應「一次編譯,隨處運行」的狀況,須要作一個翻譯動做,因而就用了JVM 本身的命令集,這與彙編的命令集有點相似,每一種彙編命令集針對一個系列的CPU ,好比8086 系列的彙編也是能夠用在8088 上的,可是就不能跑在8051 上,而JVM 的命令集則是能夠處處運行的,由於JVM 作了翻譯,根據不一樣的CPU ,翻譯成不一樣的機器語言。面試

  JVM 中咱們最須要深刻理解的就是它的存儲部分,存儲?硬盤?NO ,NO , JVM 是一個內存中的虛擬機,那它的存儲就是內存了,咱們寫的全部類、常量、變量、方法都在內存中,這決定着咱們程序運行的是否健壯、是否高效,接下來的部分就是重點介紹之。   編程

2,JVM組成(認真閱讀)  

該圖參考了網上廣爲流傳的JVM 構成圖,你們看這個圖,整個JVM 分爲四部分:安全

Class Loader 類加載器

類加載器的做用是加載類文件到內存,好比編寫一個HelloWord.java 程序,而後經過javac 編譯成class 文件,那怎麼才能加載到內存中被執行呢?Class Loader 承擔的就是這個責任,那不可能隨便創建一個.class 文件就能被加載的,Class Loader 加載的class 文件是有格式要求,在《JVM Specification 》中式這樣定義Class 文件的結構:bash

ClassFile {
      u4 magic;
      u2 minor_version;
       u2 major_version;
      u2 constant_pool_count;
      cp_info constant_pool[constant_pool_count-1];
      u2 access_flags;
      u2 this_class;
      u2 super_class;
      u2 interfaces_count;
      u2 interfaces[interfaces_count];
      u2 fields_count;
      field_info fields[fields_count];
      u2 methods_count;
      method_info methods[methods_count];
      u2 attributes_count;
      attribute_info attributes[attributes_count];
    }
複製代碼

須要詳細瞭解的話,能夠仔細閱讀《JVM Specification 》的第四章「The class File Format 」,這裏再也不詳細說明。框架

友情提示:Class Loader 只管加載,只要符合文件結構就加載,至於說能不能運行,則不是它負責的,那是由Execution Engine 負責的。jvm

Execution Engine 執行引擎

執行引擎也叫作解釋器(Interpreter) ,負責解釋命令,提交操做系統執行。編程語言

Native Interface 本地接口

本地接口的做用是融合不一樣的編程語言爲Java 所用,它的初衷是融合C/C++ 程序,Java 誕生的時候是C/C++ 橫行的時候,要想立足,必須有一個聰明的、睿智的調用C/C++ 程序,因而就在內存中專門開闢了一塊區域處理標記爲native 的代碼,它的具體作法是Native Method Stack 中登記native 方法,在Execution Engine 執行時加載native libraies 。目前該方法使用的是愈來愈少了,除非是與硬件有關的應用,好比經過Java 程序驅動打印機,或者Java 系統管理生產設備,在企業級應用中已經比較少見,由於如今的異構領域間的通訊很發達,好比可使用Socket 通訊,也可使用Web Service 等等,很少作介紹。this

Runtime data area 運行數據區

運行數據區是整個JVM 的重點。咱們全部寫的程序都被加載到這裏,以後纔開始運行,Java 生態系統如此的繁榮,得益於該區域的優良自治。

整個JVM 框架由加載器加載文件,而後執行器在內存中處理數據,須要與異構系統交互是能夠經過本地接口進行,瞧,一個完整的系統誕生了!

三、JVM加載class文件的原理機制 (好好記住)

Java中的全部類,都須要由類加載器裝載到JVM中才能運行。類加載器自己也是一個類,而它的工做就是把class文件從硬盤讀取到內存中。在寫程序的時候,咱們幾乎不須要關心類的加載,由於這些都是隱式裝載的,除非咱們有特殊的用法,像是反射,就須要顯式的加載所須要的類。

類裝載方式,有兩種

1.隱式裝載, 程序在運行過程當中當碰到經過new等方式生成對象時,隱式調用類裝載器加載對應的類到jvm中

2.顯式裝載, 經過class.forname()等方法,顯式加載須要的類

隱式加載與顯式加載的區別:二者本質是同樣? Java類的加載是動態的,它並不會一次性將全部類所有加載後再運行,而是保證程序運行的基礎類(像是基類)徹底加載到jvm中,至於其餘類,則在須要的時候才加載。這固然就是爲了節省內存開銷。

Java的類加載器有三個

對應Java的三種類:

1.系統類

2.擴展類

3.由程序員自定義的類

Bootstrap Loader  // 負責加載系統類 (指的是內置類,像是String,對應於C#中的System類和C/C++標準庫中的類)
        | 
      - - ExtClassLoader   // 負責加載擴展類(就是繼承類和實現類)
                      | 
                  - - AppClassLoader   // 負責加載應用類(程序員自定義的類)
複製代碼

三個加載器各自完成本身的工做,但它們是如何協調工做呢?哪個類該由哪一個類加載器完成呢?爲了解決這個問題,Java採用了委託模型機制。

委託模型機制的工做原理很簡單:當類加載器須要加載類的時候,先請示其Parent(即上一層加載器)在其搜索路徑載入,若是找不到,纔在本身的搜索路徑搜索該類。這樣的順序其實就是加載器層次上自頂而下的搜索,由於加載器必須保證基礎類的加載。之因此是這種機制,還有一個安全上的考慮:若是某人將一個惡意的基礎類加載到jvm,委託模型機制會搜索其父類加載器,顯然是不可能找到的,天然就不會將該類加載進來。

咱們能夠經過這樣的代碼來獲取類加載器:

ClassLoader loader = ClassName.class.getClassLoader();
ClassLoader ParentLoader = loader.getParent();
複製代碼

注意一個很重要的問題,就是Java在邏輯上並不存在BootstrapKLoader的實體!由於它是用C++編寫的,因此打印其內容將會獲得null。

前面是對類加載器的簡單介紹,它的原理機制很是簡單,就是下面幾個步驟:

1.裝載:查找和導入class文件;

2.鏈接:

(1)檢查:檢查載入的class文件數據的正確性;

  (2)準備:爲類的靜態變量分配存儲空間;

  (3)解析:將符號引用轉換成直接引用(這一步是可選的)
複製代碼

3.初始化:初始化靜態變量,靜態代碼塊。

這樣的過程在程序調用類的靜態成員的時候開始執行,因此靜態方法main()纔會成爲通常程序的入口方法。類的構造器也會引起該動做。


稍微詳細說下,加載步驟

裝載

簡單描述:在Java程序運行以前JVM會把編譯完成的.class二進制文件加載到內存,後續提供程序使用,用到的就是類加載器ClassLoader 。加載階段與鏈接階段的部份內容(如一部分字節碼文件格式驗證動做)是交叉進行的,加載階段還沒有結束,鏈接階段就可能開始了。可是夾在加載階段進行的動做,仍然屬於鏈接階段的內容。

鏈接

  • 鏈接 - 驗證  

    驗證是鏈接的第一步,目的是爲了確保Class文件的字節流中包含的信息符合當前虛擬機的要求,而且不會危及虛擬機自己的安全。 驗證階段的四個步驟:文件格式檢驗、元數據檢驗、字節碼檢驗、符號引用檢驗。

文件格式檢驗:檢驗字節流是否符合Class文件格式的規範,而且能被當前版本的虛擬機處理。

元數據檢驗:對字節碼描述的信息進行語義分析,以保證其描述的內容符合Java語言規範的要求.

字節碼檢驗:經過數據流和控制流分析,肯定程序語義是合法、符合邏輯的。

符號引用檢驗:符號引用檢驗能夠看做是對類自身之外(常量池中的各類符號引用)的信息進行匹配性校驗。
複製代碼
  • 鏈接 - 準備

    該階段正式爲類變量分配內存並設置類變量初始值。這些變量所使用的內存將在方法區中進行分配。此時進行內存分配的僅包括類變量,而不包括實例變量(實例變量將會在對象實例化時隨着對象一塊兒分配在Java堆中)。另外,在這裏分配的靜態類變量是將其值定義爲默認值。由於在該階段並未執行任何Java方法,正確的賦值將在初始化階段執行。

  • 鏈接 - 解析

    該階段虛擬機會將常量池內的符號引用替換爲直接引用的過程。

初始化

這是類加載的最後一步,真正執行類中定義的字節碼,也就是.class文件。 初始化階段是執行類構造器方法的過程,以及真正初始化類變量和其餘資源的過程。

相關文章
相關標籤/搜索