Java代碼是運行在Java虛擬機(JVM)上的,Java虛擬機經過解釋執行(解釋器)或編譯執行(編譯器)來完成。html
Java內存模型分爲5個部分:方法區(Method Area),Java堆(Heap),Java棧(VM Stack),本地方法棧(Native Method Stack),程序計數器(PC 寄存器)java
(圖片來源:http://gityuan.com/images/jvm/jvm_memory_1.png)git
線程共享區:數組
方法區(Method Area):方法區是各個線程共享的區域,存放類信息,常量,靜態常量,編譯器編譯後的代碼等信息。安全
Java堆(Heap):Java堆也是線程共享區域,類的實例存放在這裏,一個系統會產生不少Java實例,所以Java堆的空間是最大的,若是Java堆的空間不足,就會拋出OutOfMemoryError異常。框架
線程私有區:jvm
Java棧(VM Stack):線程私有區域,生命週期與線程相同,一個線程對應一個Java棧,每執行一個方法就會向棧裏壓一個元素,這個元素叫「棧幀」,棧幀中包含了方法中保存了該方法調用的參數、局部變量和返回地址等信息,若是棧空間不足了就會拋出StackOverflowError異常。jsp
本地方法棧(Native Method Stack):和Java棧相似,本地方法棧是用來執行本地方法的,存放的方法調用本地方法接口,最終調用本地方法庫,實現與操做系統,硬件交互的目的。ide
程序計數器:這裏對應的類以及加載,實例對象,方法,靜態變量去了該去的地方,那麼問題來了,程序該怎麼執行,哪一個方法先執行,哪一個方法後執行,這些指令執行的順序就是PC寄存器在管,它的做用就是控制程序指令的執行順序。編碼
編寫的java代碼會經過編譯器編譯成字節編碼的.class文件,再把字節編碼加載到JVM中,映射到內存的各個區域中,程序就能夠在內存中運行了。
類加載流程
(圖片來源:https://images2017.cnblogs.com/blog/352511/201708/352511-20170825174319746-900347526.png)
加載是類裝載的第一步,內存中生成一個表明這個類的java.lang.class對象,經過class文件的路徑讀取到二進制流,並解析二進制裏的元數據(類型,常量等),做爲方法區這個類的各類數據量的入口;這裏的不必定從class文件獲取,這裏既能夠從ZIP包(jar,war)包中獲取,也在運行時動態生成(jsp轉換成class文件,動態代理生成)。
鏈接又可分爲驗證,準備,解析。
2.1,驗證
驗證主要是判斷class文件的合法性,對版本號進行驗證(例如若是使用java1.8編譯後的class文件要再java1.6虛擬機上運行),還會對元數據,字節編碼等進行驗證,確保class文件裏的字節流信息符合當前虛擬機的要求,不會危害虛擬機的安全。
2.2,準備
準備主要是分配內存,爲變量分配初始值,即在方法區中分配這些變量所使用的內存空間,例如:
public static int i = 1;
在準備階段i的值會被初始化爲0,後面的類的初始化階段纔會賦值爲1;
public static final int i = 1;
對應常量(static final)i,在準備階段就會被賦值1;
2.3,解析
解析就是把代碼中的符號引用替換爲直接引用;例如某個類繼承了java.lang.Object,原來的符號引用記錄的是「java.lang.Object」,並非java.lang,Object對象,直接引用就是找出對應的java.lang.Object對應的內存地址,創建直接引用關係;
初始化的過程包括執行類構造器方法,static變量賦值語句,static{}代碼塊,若是是一個子類進行初始化會先對其父類進行初始化,保證其父類在子類以前進行初始化;因此其實在java中初始化一個類,那麼必然是先初始化java.lang.Object,由於全部的java類都繼承自java.lang.Object。
如下幾種狀況不會執行類初始化:
在JVM中有三中類加載器,BootStrap Classloader(啓動Classloader)、Extension Classloader(擴展Classloader)和APP Classloader(應用Classloader);
BootStrap ClassLoader主要加載JVM自身須要的類,這個加載器由C++編寫是虛擬機的一部分,負責加載 JAVA_HOME\lib 目錄中的,或經過-Xbootclasspath參數指定路徑中的,且被虛擬機承認(按文件名識別,如rt.jar)的類。
Extension Classloader是sun.misc.Launcher中的內部類ExtClassLoader,負責加載 JAVA_HOME\lib\ext 目錄中的,或經過java.ext.dirs系統變量指定路徑中的類庫。
APP ClassLoader是sun.misc.Launcher中的內部類AppClassLoader,負責加載用戶路徑上的類庫。
用戶也能夠經過繼承ClassLoader實現本身的類加載器。
當一個類加載器接收到類加載的任務時,會首先交給其父類加載器去加載,只有當父類加載器沒法加載是其纔會本身加載。其好處是能夠避免一個類被重複加載。
即便兩個類來源於相同的class文件,若是使用的類加載器不一樣,加載後的對象時徹底不一樣的,這個不一樣反應在對象的 equals()、isAssignableFrom()、isInstance()等方法的返回結果,也包括了使用 instanceof 關鍵字對對象所屬關係的斷定結果。
頂層ClassLoader,沒法加載底層ClassLoader的類
Java框架(rt.jar)如何加載應用的類?
好比:javax.xml.parsers包中定義了xml解析的類接口
Service Provider Interface SPI 位於rt.jar
即接口在啓動ClassLoader中。
而SPI的實現類,在AppLoader。
這樣就沒法用BootstrapClassLoader去加載SPI的實現類。
JDK中提供了一個方法:
1: Thread. setContextClassLoader()
用以解決頂層ClassLoader沒法訪問底層ClassLoader的類的問題;
基本思想是,在頂層ClassLoader中,傳入底層ClassLoader的實例。