標籤:
a) 執行。
main()作為該程式的初始線的起點。無論由線程開始在其他線程。
JVM有兩個內螺紋:守護線程和非守護線程,main()它是一個非守護線程。常由JVM自己使用。java程式也能夠標明自己建立的線程是守護線程
b) 消亡。當程式中的全部非守護線程都終止時,JVM才退出;若安全管理器同意,程式也能夠使用Runtime類或者System.exit()來退出
2. JVM執行引擎執行個體則相應了屬於使用者執行程式的線程它是線程層級的
一、 JVM的體繫結構
1. 類裝載器(ClassLoader)(用來裝載.class檔案)
2. 運行引擎(運行位元組碼,或者運行本地方法)
3. 執行時資料區(方法區、堆、java棧、PC寄存器、本地方法棧)
二、 JVM類載入器
JVM整個類載入過程的步驟:
1. 裝載
裝載過程負責找到二進位位元組碼並載入至JVM中,JVM通過類名、類所在的包名通過ClassLoader來完畢類的載入,相同。也採用以上三個元素來標識一個被載入了的類:類名+
包名+ClassLoader執行個體ID。
2. 連結
連結過程負責對二進位位元組碼的格式進行校正、初始化裝載類中的靜態變數以及解析類中調用的介面、類。
完畢校正後,JVM初始化類中的靜態變數。並將其值賦為預設值。
最後對類中的全部屬性、方法進行驗證,以確保其須要調用的屬性、方法存在,以及具備應的許可權(比如public、private域許可權等)。會造成NoSuchMethodError、NoSuchFieldError等錯誤資訊。
3. 初始化
初始化過程即為運行類中的靜態初始化代碼、構造器代碼以及靜態屬性的初始化,在四種情況下初始化過程會被觸發運行:
調用了new;
反射調用了類中的方法。
子類調用了初始化;
JVM啟動過程中指定的初始化類。
JVM類載入順序:
JVM兩種類裝載器包含:啟動類裝載器和使用者自己定義類裝載器。
啟動類裝載器是JVM實現的一部分;
使用者自己定義類裝載器則是Java程式的一部分,必須是ClassLoader類的子類。
JVM裝載順序:
Jvm啟動時,由Bootstrap向User-Defined方向載入類;
應用進行ClassLoader時,由User-Defined向Bootstrap方向尋找並載入類;
1. Bootstrap ClassLoader
這是JVM的根ClassLoader。它是用C++實現的。JVM啟動時初始化此ClassLoader。並由此ClassLoader完畢$JAVA_HOME中jre/lib/rt.jar(Sun JDK的實現)中全部class檔案的載入,這個jar中包括了java規範定義的全部介面以及實現。
2. Extension ClassLoader
JVM用此classloader來載入擴充功能的一些jar包。
3. System ClassLoader
JVM用此classloader來載入啟動參數中指定的Classpath中的jar包以及檔案夾,在Sun JDK中ClassLoader相應的類名為AppClassLoader。
4. User-Defined ClassLoader
User-DefinedClassLoader是Java開發人員繼承ClassLoader抽象類別自行實現的ClassLoader,基於自己定義的ClassLoader可用於載入非Classpath中的jar以及檔案夾。
ClassLoader抽象類別的幾個關鍵方法:
(1) loadClass
此方法負責載入指定名字的類,ClassLoader的實現方法為先從已經載入的類中尋找,如沒有則繼續從parent ClassLoader中尋找。如仍然沒找到。則從SystemClassLoader中尋找,最後再調用findClass方法來尋找,如要改變類的載入順序,則可覆蓋此方法
(2) findLoadedClass
此方法負責從當前ClassLoader執行個體對象的緩衝中尋找已載入的類。調用的為native的方法。
(3) findClass
此方法直接拋出ClassNotFoundException,因此須要通過覆蓋loadClass或此方法來以自己定義的方式載入對應的類。
(4) findSystemClass
此方法負責從System ClassLoader中尋找類,如未找到,則繼續從BootstrapClassLoader中尋找,如仍然為找到,則返回null。
(5) defineClass
此方法負責將二進位的位元組碼轉換為Class對象
(6) resolveClass
此方法負責完畢Class對象的連結。如已連結過。則會直接返回。
三、 JVM運行引擎
在運行方法時JVM提供了四種指令來運行:
(1)invokestatic:調用類的static方法
(2)invokevirtual:調用對象執行個體的方法
(3)invokeinterface:將屬性定義為介面來進行調用
(4)invokespecial:JVM對於初始化對象(Java構造器的方法為:<init>)以及調用對象執行個體中的私人方法時。
基本的運行技術有:
解釋。即時編譯,自適應最佳化、晶片級直接運行
(1)解釋屬於第一代JVM,
(2)即時編譯JIT屬於第二代JVM,
(3)自適應最佳化(眼下Sun的HotspotJVM採用這樣的技術)則吸取第一代JVM和第二代
JVM的經驗,採用兩者結合的方式
開始對全部的代碼都採取解釋啟動並執行方式,並監視代碼運行情況。然後對那些常常調用的方法啟動一個後台線程。將其編譯為本地代碼,並進行最佳化。若方法不再頻繁使用,則取消編譯過的代碼,仍對其進行解釋運行。
四、 JVM執行時資料區
第一塊:PC寄存器
PC寄存器是用於儲存每一個線程下一步將啟動並執行JVM指令,如該方法為native的,則PC寄存器中不儲存不論什麼資訊。
第二塊:JVM棧
JVM棧是線程私人的。每一個線程建立的同一時候都會建立JVM棧,JVM棧中存放的為當前線程中局部基本類型的變數(java中定義的八種基本類型:boolean、char、byte、short、int、long、float、double)、部分的返回結果以及Stack Frame。非基本類型的對象在JVM棧上僅存放一個指向堆上的地址
第三塊:堆(Heap)
它是JVM用來儲存物件執行個體以及數組值的地區。能夠覺得Java中全部通過new建立的對象的記憶體都在此分配。Heap中的對象的記憶體須要等待GC進行回收。
(1) 堆是JVM中全部線程共用的,因此在其上進行對象記憶體的分配均須要進行加鎖。這也導致了new對象的開銷是比較大的
(2) Sun Hotspot JVM為了提升對象記憶體配置的效率。對於所建立的線程都會分配一塊獨立的空間TLAB(Thread Local AllocationBuffer),其大小由JVM依據執行的情況計算而得,在TLAB上指派至時不須要加鎖,因此JVM在給線程的對象分配記憶體時會盡量的在TLAB上分配。在這樣的情況下JVM中指派至記憶體的效能和C基本是一樣高效的。但假設對象過大的話則仍然是直接使用堆空間分配
(3) TLAB僅作用於新生代的Eden Space,因此在編寫Java程式時,通常多個小的對象比大的對象分配起來更加高效。
第四塊:方法地區(Method Area)
(1)在Sun JDK中這塊地區相應的為PermanetGeneration,又稱為持久代。
(2)方法地區存放了所載入的類的資訊(名稱、修飾符等)、類中的靜態變數、類中定義為final類型的常量、類中的Field資訊、類中的方法資訊。當開發人員在程式中通過Class
對象中的getName、isInterface等方法來擷取資訊時,這些資料都來源於方法地區,同一時候方法地區也是全域共用的,在一定的條件下它也會被GC。當方法地區須要使用的記憶體超過其同意的大小時,會拋出OutOfMemory的錯誤資訊。
第五塊:執行時常量池(Runtime Constant Pool)
存放的為類中的固定的常量資訊、方法和Field的引用資訊等。其空間從方法地區中分配。
第六塊:本地方法堆棧(Native Method Stacks)
JVM採用本地方法堆棧來支援native方法的運行,此地區用於儲存每一個native方法調用的狀態。
五、 JVM記憶體回收
GC的基本原理:將記憶體中不再被使用的對象進行回收。GC中用於回收的方法稱為收集器,因為GC須要消耗一些資源和時間。Java在對對象的生命週期特徵進行分析後。依照新生代、舊生代的方式來對對象進行收集,以儘可能的縮短GC相應用造成的暫停
(1)對新生代的對象的收集稱為minor GC;
(2)對舊生代的對象的收集稱為Full GC;
(3)程式中主動調用System.gc()強制啟動並執行GC為Full GC。
不同的對象參考型別,GC會採用不同的方法進行回收。JVM對象的引用分為了四種類型:
(1)強引用:預設情況下。對象採用的均為強引用(這個對象的執行個體沒有其它對象引用,GC時才會被回收)
(2)軟引用:軟引用是Java中提供的一種比較適合於緩衝情境的應用(僅僅有在記憶體不夠用的情況下才會被GC)
(3)弱引用:在GC時一定將會GC恢複
(4)虛擬參考:是不是因為虛擬參考對象僅僅是為了學習GC
著作權聲明:本文部落格原創文章,部落格,未經同意,不得轉載。
Java 記憶體架構