作者 | Alan
來源 | cnblogs.com/wangjiming/p/10455993.html
對于任何一門語言,要想達到精通的水平,研究它的執(zhí)行原理(或者叫底層機制)不失為一種良好的方式。
在本篇文章中,將重點研究JAVA源代碼的執(zhí)行原理,即從程序員編寫JAVA源代碼,到最終形成產品,在整個過程中,都經歷了什么?每一步又是怎么執(zhí)行的?執(zhí)行原理又是什么?
一、編寫java源程序
java源文件:指存儲java源碼的文件。
先來看看如下代碼:
//MyTest被public修飾,故存儲該java源碼的文件名為MyTest
public class MyTest {
public static void main(String[] args){
System.out.println("Test Java execute process.");
}
}
//由于MyTest被public修飾了,故Class A不能用public修飾
class A{}
//由于MyTest被public修飾了,故Class B不能用public修飾
class B{}
1、java源文件名就是該源文件中public類的名稱
2、一個java源文件可以包含多個類,但只允許一個類為public
二、編譯java源代碼
當java源程序編碼結束后,就需要編譯器編譯。
安裝好jdk后,我們打開jdk目錄,有兩個.exe文件,即javac.exe(編譯源代碼,xxx.java文件) 和 java.exe(執(zhí)行字節(jié)碼,xxx.class文件).
如下圖所示:
1、切換到MyTest.java文件夾
2、javac.exe編譯MyTest.java
編譯后,發(fā)現e:Blogs 目錄多了以class為后綴的文件:A.class,B.class和MyTest.class
Tip:當javac.exe編譯java源代碼時,java源代碼有幾個類,就會編譯成一個對應的字節(jié)碼文件(.class文件)
其中,字節(jié)碼文件的文件名就是每個類的類名。需要注意的是,類即使不在源文件中定義,但被源文件引用,編譯后,也會編程相應的字節(jié)碼文件。
如類A引用類C,但類C不定義在類A的源文件中,編譯后,類C也被編譯成對應的字節(jié)碼文件C.class
Tips:關注微信公眾號:Java后端,每日獲取技術博文推送。
三、執(zhí)行java源文件
執(zhí)行java源文件,用java.exe執(zhí)行即可
到現在,java源程序基本執(zhí)行結果,并正確打印我們期望的結果,那么,如上的步驟,我們可以總結如下:
如上總結,已經抽象化了在JVM中的執(zhí)行。接下來,我們將分析字節(jié)碼文件(.class文件)如何在虛擬機中一步一執(zhí)行的。
四、JVM如何執(zhí)行字節(jié)碼文件
1、裝載字節(jié)碼文件
當 .java 源碼被 javac.exe 編譯器編譯成 .class 字節(jié)碼文件后,接下來的工作就交給JVM處理。
JVM首先通過類加載器(ClassLoader),將class文件和相關Java API加載裝入JVM,以供JVM后續(xù)處理。
在該階段中,涉及到如下一些基本概念和知識。
1)JDK,JRE和JVM關系
- JDK(Java Development Kit),Java開發(fā)工具包,主要用于開發(fā),在JDK7前,JDK包括JRE
- JRE(Java Runtime Environment),Java程序運行的核心環(huán)境,包括JVM和一些核心庫
- JVM(Java Virtual machine),VM是一種用于計算設備的規(guī)范,它是一個虛構出來的計算機,是通過在實際的計算機上仿真模擬各種計算機功能來實現的,是JRE核心模塊。
2)JVM
JVM是一種用于計算設備的規(guī)范,它是一個虛構出來的計算機,是通過在實際的計算機上仿真模擬各種計算機功能來實現的。
Java虛擬機的主要任務是裝載class文件,并執(zhí)行其中的字節(jié)碼,不同的Java虛擬機中,執(zhí)行引擎可能有不同的實現。
大致有如下幾種引擎:
- 一次性解釋字節(jié)碼引擎
- 即時編譯引擎
- 自適應優(yōu)化器
關于虛擬機的實現方式,采用軟件方式、硬件方式和軟件硬件結合方式,這個要根據具體廠商而定。
3)什么是ClassLoader
虛擬機的主要任務是裝載class文件并執(zhí)行其中的字節(jié)碼,而class文件是由虛擬機的類加載器(ClassLoader)完成的,在一個Java虛擬機中有可能存在多個類加載器。
任何java運用程序,可能會使用兩種類加載器,即啟動類加載器(bootstrap)和用戶自定義類加載器。
啟動類加載器是Java虛擬機唯一實現的一部分,它又可分為原始類裝載器,系統類裝載器或默認類裝載器。它的主要作用是從操作系統的磁盤裝載相應的類,如Java API類等。
用戶自定義裝載類,即按照用戶自定義的方式來裝載類。
2、將字節(jié)碼文件存儲在JVM內存區(qū)
當JAVA虛擬機運行一個程序時,它需要內存來存儲許多東西。
比如如字節(jié)碼,程序創(chuàng)建的對象,傳遞給方法的參數,返回值,局部變量以及運算的中間結果等,這些相關信息被組織到“運行時數據區(qū)”。
根據廠商的不同,在Java虛擬機中,運行時數據區(qū)也有所不同。有些運行時數據區(qū)由線程共享,有些只能由某個特定線程共享。
運行時數據區(qū)大致可分幾個區(qū):方法區(qū),堆區(qū),棧區(qū),PC寄存器區(qū)和本地方法棧區(qū)。
在該階段中,涉及到如下基本概念和知識。
1)方法區(qū)
方法區(qū)用來存儲解析被加載的class文件的相關信息。
當虛擬裝載一個class文件后,它會從這個class文件包含的二進制數據中解析類型信息,然后將該相關信息存儲到方法區(qū)中。
2)堆
堆是用來存儲相關引用類型的,如new對象。當程序運行時,虛擬機會把所有該程序在運行時創(chuàng)建的對象都放到堆中。
3)PC寄存器
PC寄存器主要用來存儲線程。當新創(chuàng)建一個線程時,該線程都將得到一個自己的PC寄存器(程序計數器)以及一個java棧。
Java虛擬機沒有寄存器,其指令集使用Java棧來存儲中間數據。
4)棧區(qū)
棧區(qū)主要用來存儲值類型的,如基本數據類型。需要注意的是,String為引用類型,是存在堆中的。
Java棧是由許多棧幀組成的,一個棧幀包含一個Java方法調用的狀態(tài),當線程調用一個方法時,虛擬機壓入一個新的棧幀到該線程的Java棧中,當該方法返回時,這個棧幀從Java棧中彈出。
3、執(zhí)行引擎與運行時數據區(qū)交互
運行時數據區(qū)為執(zhí)行引擎提供了執(zhí)行環(huán)境和相關數據,執(zhí)行引擎通過與運行時數據區(qū)交互,從而獲取執(zhí)行時需要的相關信息,存儲執(zhí)行的中間結果等
4、執(zhí)行引擎與本地方法接口
當要執(zhí)行本地方法時,執(zhí)行引擎將調用本地方法接口來獲取相關OS本地方法。
需要注意的是,本地方法與操作系統強耦合的。
5、JVM在具體操作系統上執(zhí)行
JVM通過調用本地接口來獲取本地方法,從而實現在具體的平臺上執(zhí)行。比如在linux系統上執(zhí)行,在Window系統上執(zhí)行和在Unix系統上執(zhí)行。