JVM深入理解(一)
?
?
JVM是什么
JRE、JDK和JVM 的關系
JVM原理
1、JVM是什么?
?
JVM是Java Virtual Machine(Java虛擬機)的縮寫,由一套字節碼指令集、一組寄存器、一個棧、一個垃圾回收堆和一個存儲方法域等組成。
?
他是幫助我們將java代碼 生成編譯后 的 class 文件。?
?
?
?
2、JRE、JDK和JVM 的關系
?
JRE(Java Runtime Environment, Java運行環境)是Java平臺,所有的程序都要在JRE下才能夠運行。包括JVM和Java核心類庫和支持文件。
?
JDK(Java Development Kit,Java開發工具包)是用來編譯、調試Java程序的開發工具包。包括Java工具(javac/java/jdb等)和Java基礎的類庫(java API )。
?
JVM(Java Virtual Machine, Java虛擬機)是JRE的一部分。JVM主要工作是解釋自己的指令集(即字節碼)并映射到本地的CPU指令集和OS的系統調用。Java語 言是跨平臺運行的,不同的操作系統會有不同的JVM映射規則,使之與操作系統無關,完成跨平臺性。
?
?
?
JDK
?
JDK(Java Development Kit) 是 Java 語言的軟件開發工具包(SDK)。JDK 物理存在,是 programming tools、JRE 和 JVM 的一個集合。
?
JRE
?
JRE(Java Runtime Environment)Java 運行時環境,JRE 物理存在,主要由Java API 和 JVM 組成,提供了用于執行 java 應用程序最低要求的環境。
?
?
?
3、JVM原理
?
3.1、JVM的體系結構
?
3.2、JVM生命周期介紹
?
3.2.1 啟動:
?
啟動一個JAVA程序,一個JVM實例就會產生。例如我們通常用到的main() 方法一樣。
?
3.2.2 運行:
?
用main() 作為程序初始線程的起點,任何其他線程均可由該線程啟動。JVM內部有兩種線程:守護線程和非守護線程。
?
main() 屬于非守護線程,守護線程通常由JVM使用,程序可以指定創建的線程為守護線程。
?
3.2.3 消亡:
?
當程序中的所有非守護線程都終止時,JVM才退出;若安全管理器允許,程序也可以使用Runtime類或者System.exit退出。
?
JVM執行引擎實例則對應了屬于用戶運行程序線程它是線程級別的。
?
?
?
3.3、JAVA類加載器
?
Java加載類的過程:
?
3.3.1、裝載(Loading)
?
負責找到二進制字節碼并加載到JVM中,JVM通過類名、類所在的包名、ClassLoader完成類的加載。因此,標識一個被加載了的類:類名 + 包名 + ClassLoader實例ID。
?
3.3.2、鏈接(Linking)
?
負責對二進制字節碼的格式進行校驗、初始化裝載類中的靜態變量以及解析類中調用的接口。
?
完成校驗后,JVM初始化類中的靜態變量,并將其賦值為默認值。
?
最后對比類中的所有屬性、方法進行驗證,以確保要調用的屬性、方法存在,以及具備訪問權限(例如private、public等),否則會造成NoSuchMethodError、 NoSuchFieldError等錯誤信息。
?
3.3.3、初始化(Initializing)
負責執行類中的靜態初始化代碼、構造器代碼以及靜態屬性的初始化,以下四種情況初始化過程會被觸發。
?
?
3.4、JVM類加載順序
?
層級結構
?
1.Booststrap ClassLoader
?
跟ClassLoader,C++實現,JVM啟動時初始化此ClassLoader,并由此完成$JAVA_HONE中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-Defined ClassLoader是Java開發人員繼承ClassLoader抽象類實現的ClassLoader,基于自定義的ClassLoader可用于加載非ClassPath中的jar以及目錄。
?
?
3.5、委派模式(Delegation Mode)
?
當JVM加載一個類的時候,下層的加載器會將任務給上一層類加載器,上一層加載檢查它的命名空間中是否已經加載這個類,如果已經加載,直接使用這個類。如果沒有加載,繼續往上委托直到頂部。檢查之后,按照相反的順序進行加載。如果Bootstrap加載器不到這個類,則往下委托,直到找到這個類。一個類可以被不同的類加載器加載。
?
可見性限制:下層的加載器能夠看到上層加載器中的類,反之則不行,委派只能從下到上。
?
不允許卸載類:類加載器可以加載一個類,但不能夠卸載一個類。但是類加載器可以被創建或者刪除。
?
?
?
3.6、JVM執行引擎
?
類加載器將字節碼載入內存后,執行引擎以java字節碼為單元,讀取java字節碼。java字節碼機器讀不懂,必須將字節碼轉化為平臺相關的機器碼。這個過程就是由執行引擎完成的。
?
在執行方法時JVM提供了四種指令來執行
?
invokestatic:調用類的static方法。
?
invokevirtual:調用對象實例的方法。
?
invokeinterface:將屬性定義為接口來進行調用。
?
invokespecial:JVM對于初始化對象(Java構造器的方法為:)以及調用對象實例的私有方法時。
?
主要的執行計數:
?
解釋,即時執行,自適應優化、芯片級直接執行。
?
解釋屬于第一代JVM
?
即時編譯JIT屬于第二代JVM
?
自適應優化(目前sun的HotspotJVM采用這種技術),吸取第一代JVM和第二代JVM的經驗,采用兩者結合的方式,開始對所有的代碼都采用解釋執行的方式,并監視代碼執行情況,然后對那些經常調用的方法啟動一個后臺線程,將其編譯為本地代碼,并進行優化。若方法不再頻繁使用,則取消編譯過代碼,仍對其進行解釋執行。
?
?
3.7、Java運行時數據區
?
PC寄存器
?
用于存儲每個線程下一步將要執行的JVM指令,若該方法為native的,則PC寄存器中不存儲任何信息。Java多線程情況下,每個線程都有一個自己的PC,以便完成不同線程上下文環境的切換
?
JVM棧
?
JVM棧是線程私有的,每個線程創建的同時都會創建JVM棧,JVM棧中存放當前線程中局部基本類型的變量(Java中定義的八種基本類型:boolean、char、byte、short、int、long、float、double)、部分的返回結果以及Stack Frame,非基本類型的對象在JVM棧上僅存放一個指向堆的地址。
堆(Heap)
?
它是JVM用來存儲對象實例以及數組值的區域,可以認為Java中所有通過new創建的對象的內存都在此分配,Heap中的對象的內存需要等待GC進行回收。
?
堆在JVM啟動的時候就被創建,堆中儲存了各種對象,這些對象被自動管理內存系統(Automatic Storage Management System),也就是常說的“Garbage Collector(垃圾回收器)”管理。這些對象無需、也無法顯示地被銷毀。
?
JVM將Heap分為兩塊:新生代New Generation和舊生代Old Generation
?
堆是JVM中所有線程共享的,因此在其上進行對象內存的分配均需要進行加鎖,導致new對象的開銷比較大。
?
Sun Hotspot JVM為了提升對象內存分配的效率,對于所有創建的線程都會分配一塊獨立的空間TLAB(Thread Local Allocation Buffer),其大小由JVM根據運行的情況計算而得,在TLAB上分配對象時不需要加鎖,因此JVM在給線程對象分配內存時會盡量的在TLAB上分配,在這種情況下JVM中分配對象內存的性能和C基本是一樣的,但如果對象過大的話則仍然要直接使用堆空間分配。
?
TLAB僅作用于新生代的Eden Space,因此在編寫Java程序時,通常多個小的對象比大的對象分配起來更加高效。
?
所有新創建的Object都將會存儲在新生代Young Generation中。如果Young Generation的數據在一次或多次GC后存活下來,那么將被轉移到OldGeneration。新的Object總是創建在Eden Space。
?
方法區域(Method Area)
在Sun JDK中這塊區域對應的為PermanetGeneration,又稱為持久代。
?
方法區域存放所加載類的信息(名稱、修飾符等)、類中的靜態變量、類中定義為final類型的常量、類中的Field信息、類中的方法信息,當開發人員在程序中通過Class對象中的getName,isInstance等方法來獲取信息時,這些數據都來源于方法區域,同時方法區域也是全局共享的,在一定條件下它也會被GC,當方法區域需要使用的內存超過其允許的大小時,就會拋出OutOfMemory的錯誤信息。
?
運行時常量池(Runtime Constant Pool)
?
存放的為類中的固定常量信息、方法和Field的引用信息等,其空間從方法區域中分配。
?
本地方法堆棧(Native Method Stacks)
?
JVM采用本地方法堆來支持native方法的執行,此區域用于存儲每個native方法調用的狀態。
?
3.8、JVM垃圾回收
GC的基本原理:將內存中不再被使用的對象進行回收,GC中用于回收的方法稱為收集器,由于GC需要消耗一些資源和時間,Java在對對象生命周期特征進行分析后,按照新生代、舊生代的方式來對對象進行收集,以盡可能的縮短GC對應用造成的暫停。
?
對新生代的對象收集稱為minor GC
?
對舊生代的對象收集稱為Full GC
?
程序中主動調用System.gc()強制執行的GC為Full GC。
?
不同的對象引用類型,GC會采用不同的方法進行回收,JVM對象的引用分為了四種類型:
?
強引用:默認情況下,對象采用的均為強引用(這個對象的實例沒有其他對象引用時, GC時才會被回收)
?
軟引用:軟引用是Java中提供的一種比較適合于緩存場景的應用(只有內存不夠的情況下才會被GC)
?
弱引用:在GC時一定會被GC回收。
?