1. 程式人生 > >Java虛擬機器-Java程式執行流程

Java虛擬機器-Java程式執行流程

一.概述

程式執行流程我把它劃分為以下幾個步驟:編輯原始碼、編譯生成class檔案、(載入class檔案、執行class位元組碼檔案),其中後兩個步驟都是在jvm虛擬機器上執行的。 


二.編輯

編輯原始碼,在任何一個工具上編寫原始碼,可以是記事本,最後命名為Student.java。


/**
 建立日期:2018.1.13
 建立人:zzg
 功能概述:Test
*/
class Person {
	
	private String name;
	private int age;
	
	public Person (int age, String name) {
		this.name = name;
		this.age = age;
	}
	
	public void run () {
		
	}
}

interface IStudyable {
	public int study (int a, int b);
}

public class Student extends Person implements IStudyable{
	
	private static int count = 5;
	private String sid;
	
	static {
		count ++;
	}
	
	public Student (int age, String name, String sid) {
		super(age, name);
		this.sid = sid;
	}
	
	@Override
	public void run () {
		System.out.println("run...");
	}
	
	@Override
	public int study (int a, int b) {
		int c = 10;
		int d = 20;
		return a+b*c-d;
	}
	
	public static int getCount () {
		return count;
	}
	
	public static void main (String[] args) {
		Student zzg = new Student(24, "zzg", "057655");
		zzg.study(5,6);
		Student.getCount();
		zzg.run();
	}
	
}
三.編譯

編譯生成class位元組碼檔案,在桌面上,按住shift,然後按下滑鼠右鍵:

四.位元組碼檔案

位元組碼檔案,看似很微不足道的東西,卻真正實現了java語言的跨平臺。各種不同平臺的虛擬機器都統一使用這種相同的程式儲存格式。更進一步說,jvm執行的是class位元組碼檔案,只要是這種格式的檔案就行,所以,實際上jvm並不像我之前想象地那樣與java語言緊緊地捆綁在一起。如果非常熟悉位元組碼的格式要求,可以使用二進位制編輯器自己寫一個符合要求的位元組碼檔案,然後交給jvm去執行;或者把其他語言編寫的原始碼編譯成位元組碼檔案,交給jvm去執行,只要是合法的位元組碼檔案,jvm都會正確地跑起來。所以,它還實現了跨語言……

通過jClassLib可以直接檢視一個.class檔案中的內容,也可以給JDK中的javap命令指定引數,來檢視.class檔案的相關資訊:

javap –v Student

可以輸出重定向下:javap –v Student > Student.class.txt


部分class檔案內容,從上面圖中,可以看到這些資訊來自於Student.class,編譯自Student.java,編譯器的主版本號是52,也就是jdk1.8,這個類是public,然後是存放類中常量的常量池,各個方法的位元組碼等,這裡就不一一記錄了。總之,位元組碼檔案很簡單很強大,它存放了這個類的各種資訊:欄位、方法、父類、實現的介面等各種資訊。

五.執行

Java虛擬機器要執行位元組碼指令,就要先載入位元組碼檔案,誰來載入,怎麼載入,載入到哪裡……誰來執行,怎麼執行,同樣也要考慮……

六.Java虛擬機器的基本結構及其記憶體分割槽


JVM中把記憶體分為直接記憶體、方法區、Java棧、Java堆、本地方法棧、PC暫存器等。

       直接記憶體:就是原始的記憶體區

       方法區:用於存放類、介面的元資料資訊,載入進來的位元組碼資料都儲存在方法區

       Java棧:執行引擎執行位元組碼時的執行時記憶體區,採用棧幀的形式儲存每個方法的呼叫執行資料

       本地方法棧:執行引擎呼叫本地方法時的執行時記憶體區

       Java堆:執行時資料區,各種物件一般都儲存在堆上

       PC暫存器:功能如同CPU中的PC暫存器,指示要執行的位元組碼指令。

       JVM的功能模組主要包括類載入器、執行引擎和垃圾回收系統

3.類載入器載入Student.class到記憶體:

       1)類載入器會在指定的classpath中找到Student.class這個檔案,然後讀取位元組流中的資料,將其儲存在方法區中。
       2)會根據Student.class的資訊建立一個Class物件,這個物件比較特殊,一般也存放在方法區中,用於作為執行時訪問Student類的各種資料的介面。
       3)必要的驗證工作,格式、語義等
       4)為Student中的靜態欄位分配記憶體空間,也是在方法區中,並進行零初始化,即數字型別初始化為0,boolean初始化為false,引用型別初始化為null等。
在Student.java中只有一個靜態欄位:
 private static int cnt=5; 
此時,並不會執行賦值為5的操作,而是將其初始化為0。
       5)由於已經載入到記憶體了,所以原來位元組碼檔案中存放的部分方法、欄位等的符號引用可以解析為其在記憶體中的直接引用了,而不一定非要等到真正執行時才進行解析。
       6)在編譯階段,編譯器收集所有的靜態欄位的賦值語句及靜態程式碼塊,並按語句出現的順序拼接出一個類初始化方法<clinit>()。此時,執行引擎會呼叫這個方法對靜態欄位進行程式碼中編寫的初始化操作。
在Student.java中關於靜態欄位的賦值及靜態程式碼塊有兩處:


可以通過jClassLib這個工具看到生成的<clinit>()方法的位元組碼指令或者命令列形式:


iconst_5指令把常數5入棧
putstatic #6將棧頂的5賦值給Student.cnt這個靜態欄位
getstatic #6 獲取Student.cnt這個靜態欄位的值,並將其放入棧頂
iconst_1 把常數1入棧
iadd 取出棧頂的兩個整數,相加,結果入棧
putstatic #6 取出棧頂的整數,賦值給Student.cnt
return 從當前方法中返回,沒有任何返回值。
從位元組碼來看,確實先後執行了cnt =5 及 cnt++這兩行程式碼。

在這裡有一點要注意的是,這裡籠統的描述了下類的載入及初始化過程,但是,實際中,有可能只進行了類載入,而沒有進行初始化工作,原因就是在程式中並沒有訪問到該類的欄位及方法等。
此外,實際載入過程也會相對來說比較複雜,一個類載入之前要載入它的父類及其實現的介面:載入的過程可以通過java –XX:+TraceClassLoading引數檢視:
如:java -XX:+TraceClassLoading Student,資訊太多,可以重定向下:


 可以看到最先載入的是Object.class這個類---所有類的父類。
 直到第390行才看到自己定義的部分被載入,先是Student實現的介面IStudyable,然後是其父類Person,然後才是Student自身,然後是一個啟動類的載入,然後就是找到main()方法,執行了。 

七.執行引擎找到main()這個入口方法,執行其中的位元組碼指令:

要了解方法的執行,需要先稍微瞭解下java棧:JVM中通過java棧,儲存方法呼叫執行的相關資訊,每當呼叫一個方法,會根據該方法的在位元組碼中的資訊為該方法建立棧幀,不同的方法,其棧幀的大小有所不同。棧幀中的記憶體空間還可以分為3塊,分別存放不同的資料:

區域性變量表:存放該方法呼叫者所傳入的引數,及在該方法的方法體中建立的區域性變數。

運算元棧:用於存放運算元及計算的中間結果等。

其他棧幀資訊:如返回地址、當前方法的引用等。

只有當前正在執行的方法的棧幀位於棧頂,當前方法返回,則當前方法對應的棧幀出棧,當前方法的呼叫者的棧幀變為棧頂;當前方法的方法體中若是呼叫了其他方法,則為被呼叫的方法建立棧幀,並將其壓入棧頂。注意:區域性變量表及運算元棧的最大深度在編譯期間就已經確定了,儲存在該方法位元組碼的Code屬性中。


檢視Student.main()的執行過程--看下main()方法:


八.總結

一個類檔案首先載入到方法區,一些符號引用被解析(靜態解析)為直接引用或者等到執行時分派(動態繫結),經過一系列的載入過程(class檔案的常量池被載入到方法區的執行時常量池,各種其它的靜態儲存結構被載入為方法區執行時資料解構等等)

然後程式通過Class物件來訪問方法區裡的各種型別資料,當載入完之後,程式發現了main方法,也就是程式入口,那麼程式就在棧裡建立了一個棧幀,逐行讀取方法裡的程式碼所轉換為的指令,而這些指令大多已經被解析為直接引用了,那麼程式通過持有這些直接引用使用指令去方法區中尋找變數對應的字面量來進行方法操作。

操作完成後方法返回給呼叫方,該棧幀出棧。記憶體空間被GC回收,堆裡被new的那些也就被來及回收機制GC了。

全流程包括以下幾步:原始碼編寫–編譯(javac編譯和jit編譯,java語法糖)—類檔案被載入到虛擬機器(類Class檔案結構,虛擬機器執行時記憶體分析,類載入機制)—-虛擬機器執行二進位制位元組碼(虛擬機器位元組碼執行系統)—垃圾回收(JVM垃圾回收機制)

相關推薦

Java虛擬機器-Java程式執行流程

一.概述程式執行流程我把它劃分為以下幾個步驟:編輯原始碼、編譯生成class檔案、(載入class檔案、執行class位元組碼檔案),其中後兩個步驟都是在jvm虛擬機器上執行的。 二.編輯編輯原始碼,在任何一個工具上編寫原始碼,可以是記事本,最後命名為Student.java

JAVA虛擬機器結構之執行時資料區

jvm的執行時資料區根據用途一共可以分為這幾類:pc寄存機,java虛擬機器棧,java堆,方法區,執行時常量池,本地方法棧。其中java堆,方法區,執行時常量是公有的資料區,隨著虛擬機器的啟動而建立,隨著虛擬的退出而銷燬。而pc暫存器,java虛擬機器棧,本地方法棧則是執行緒私有的

弄清Java虛擬機器GC的執行過程

前言:要弄清Java虛擬機器GC的整個過程,就得弄明白Java虛擬機器用什麼來進行GC?Java虛擬機器在哪裡GC?什麼時候GC?GC什麼? 開門見山 GC(Garbage Collection)垃圾收集,JVM一個非常重要的功能。本文將圍繞著JVM的GC這個動作展開,來過一遍GC的整個運作過程。 JV

jdk原始碼解析(八)——Java虛擬機器位元組碼執行引擎

在前面我們瞭解了jvm執行時資料區,那個jvm圖中有執行引擎,那麼今天就解釋一下Java虛擬機器位元組碼執行引擎。 1 定義 Java虛擬機器位元組碼執行引擎是jvm最核心的組成部分之一,“虛擬機器” 是一個相對於 “物理機” 的概念,這兩種機器都有程式碼執行能力,其區別是物理機的執行

深入理解java虛擬機器----java記憶體模型與執行

12.1  概述 衡量一個服務效能的高低好壞,每秒事務處理數是最重要的指標之一,它 著一秒內服務端平均能響應的請求總數,而TPS的與程式的併發能力又有非常密切的關係。 12.2 硬體的效率與一致性 因為有快取一致性,所以要有一些操作來保證安全。 12.3 java記憶

java虛擬機器學習之執行緒共享記憶體區和執行緒私有區

 執行緒共享指的就是可以允許被所有執行緒共享訪問的一塊記憶體,包括堆區,方法區和執行時常量池。  1. java堆區      java堆區在虛擬機器啟動時被建立,並且他在實際記憶體中是可以不連續的。

Java虛擬機器之----瞭解“執行時資料區域”

一、 前言 對於Java程式設計師來說,在虛擬機器自動記憶體管理機制的幫助下,不再需要像C/C++那樣為每一個new操作去寫配對的delete/free程式碼,不容易出現記憶體洩漏和記憶體溢位問題。 由虛擬機器管理記憶體雖然方便了程式設計師,不過,一旦出現這方面的問題,我們也必須學會

Java虛擬機器——Java記憶體模型與執行緒 [待更新]

12.2硬體的效率與一致性 處理器與記憶體速度矛盾--> 1.引入快取記憶體-->新的問題:快取一致性(Cache Coherence) 2.指令重排優化( Instruc

深入理解Java虛擬機器 位元組碼執行引擎

執行時棧幀結構 在JVM基本框架中已經提到過棧幀Frame結構。 區域性變量表 區域性變量表以Slot為基本單位,int,float,reference,boolean, byte都佔1 Slot;long和double資料被切割成連續2 Slot

java虛擬機器位元組碼執行引擎之解釋執行

java語言中,Javac編譯器完成了程式程式碼經過詞法分析,語法分析到抽象語法樹,再遍歷語法樹生成線性的位元組碼指令流的過程.這一部分動作是在java虛擬機器外部完成的,而直譯器是在虛擬機器內部的,Java程式的編譯是半獨立的實現. 直譯器負責對Javac編譯器輸出的指令

Java 虛擬機器中的執行時資料區分析

> 本文基於 JDK1.8 闡述分析 ## 執行過程 我們都知道 Java 原始檔通過編譯器編譯後,能產生相應的 .Class 檔案,也就是位元組碼檔案。而位元組碼檔案通過 Java 虛擬機器中的直譯器,編譯成特定機器上的機器碼。 ## 跨平臺的特性 ![在這裡插入圖片描述](https://i

java虛擬機器—-java記憶體區域與記憶體溢位異常

一,java虛擬機器所管理的執行時資料區域分為:程式計數器、java虛擬機器棧、本地方法棧、java堆、方法區、執行時常量池。                     1,程式計數器:

Java虛擬機器——Java記憶體區域

1、執行時區域   Java虛擬機器在執行Java程式的時候會把它管理的內厝劃分為若干個不同功能的資料區域,如圖所示 首先是程式計數器,程式計數器可以理解為當前程式執行的位元組碼的行號指示器,計數器中的資料即是下一條將要執行的位元組碼指令的行號。因為Java虛擬機器的多執行緒是通過輪流切換並分配

深入理解java虛擬機器---java記憶體區域與記憶體溢位異常---3垃圾回收機制GC

  一、垃圾回收---物件存活演算法:     1、引用計數器法:在物件身上放上一個計數器,當有引用則加一,引用失效則減一,為零則可回收。(無法解決物件相互引用)     2、可達性分析法(java),GC roots為起始點,從節點向下搜尋,搜尋路徑為引用鏈,不在引用鏈的物件則是可回收的物件

深入理解Java虛擬機器——Java堆測試

程式碼Test案例是對Java堆,新生代、老年代的理解和認識,對GC回收機制的應用 1.直接執行只展示Java堆記憶體的使用情況。 2.在執行之前,eclipse或Idea配置JVM執行引數(見程式碼

深入理解Java虛擬機器-Java記憶體模型閱讀筆記

記憶體模型簡介 這裡說的記憶體模型與堆疊記憶體模型不是同一回事,是定義一種變數線上程工作記憶體和主記憶體之間的工作規範。在書中描述一種如下圖所示的記憶體模型。 記憶體操作定義 變數在工作記憶體和主記憶體之間的互動操作,由圖中的8種操作完成,書中定義

深入理解java虛擬機器-Java記憶體區域與記憶體溢位異常

開發十年,就只剩下這套架構體系了! >>>   

Java程式執行機制和Java虛擬機器

1、java程式的執行Java中原始檔字尾為*.java,編譯(也就是javac命令)使*.java檔案轉換為*.class檔案,然後在計算機上執行(java命令)*.class檔案。這就是java程式的執行過程,那麼JVM(java虛擬機器)是做什麼用的哪?首先我們來看一下

JVM執行時資料區域 —— 程式計數器、Java虛擬機器棧、本地方法棧、Java堆、方法區、執行時常量池

java虛擬機器執行時資料區域的概括圖如下所示: 下面將對執行時資料區進行講解 程式計數器 1、說明:程式計數器可以看做是當前執行緒所執行的位元組碼的行號指示器。其實通俗點講就是記錄class檔案執行到哪一行 2、注意的點: (1)因為CPU執

java程式執行在沒有java虛擬機器的windows系統上

首先我們使用eclipse直接匯出的jar包。在該專案中右鍵選擇Export -->java資料夾中的 JAR file --下一步--在JAR flie出選擇JAR包檔案輸出路徑此時不要直接finish,而是點選下一步,在下一步看到mian class的時候一定要選擇mian class指定執行的類