java能够运行的原理_JAVA程序运行原理分析(一)
作為JAVA的開發人員,需要知道JAVA是如何運行的,這個需要好好思考下。
(一)class文件內容
class文件包含JAVA程序執行的字節碼,也就是說程序的執行是通過class里面的內容進行執行的。class文件內的信息嚴格按照一定的格式(虛擬機規范中的格式),緊湊排列在class文件中的二進制流,中間無任何分隔符。
① 分析class文件內的內容
文件開頭有一個0xcafebabe 16進制的特殊的標志,cafebabe就是java的class的標識。
整個class文件很多很多的內容,用肉眼肯定是無法分辨的,
② class包含的內容
這個文件是有復雜格式,專門有JVM讀里面的內容,方便閱讀源碼。
1.版本
源代碼是由java的哪個版本的編譯的。
2.訪問標志
這個類是public 還是private 。
3.常量池
常量哪些。
4.當前類
當前類的名稱,類的信息。
5.超級類
被繼承的類,類信息。
6.接口
實現的接口是什么?
7.字段
8.方法
9.屬性
(二)JVM運行時數據區
java 源代碼編譯后生成 class字節碼,然后被加載到JVM運行時數據區里面
① 方法區
存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據。虛擬機規范中的一個邏輯區域(沒有硬性的規定)。具體實現是根據不同的虛擬機來實現的。
如 oracle的Hotspot在java7中方法區放入永久代,java8放在元數據空間,并且通過GC機制對這個區域進行管理。
② 堆
對象,垃圾回收,都是在堆中。
堆內存還可以細分為:老年代,新生代(Eden,From Survivor,To Survivor)
JVM啟動時就創建了,存放對象的實例,垃圾回收期主要就是管理堆內存,內存滿了,就會出現OutOfMemroyEorror,后續在內存模型中,詳細講解。
③ 虛擬機棧
Java方法執行的內存模型:每個方法在執行的同時都會創建一個棧幀(Stack Frame)用于存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。一個方法對應一個棧幀。棧內存默認最大是1M,超出則拋出StackOverflowError。計算,保存一些信息都是在棧里面。
④ 本地方法棧
和java虛擬機棧類似,不同的是其為Native方法服務。它跟java虛擬機棧的區別就是執行的方法不同。
⑤ 程序計數器
當前線程所執行的字節碼的行號指示器,每個線程都有一個獨立的程序計數器。每個線程都在這個控件有一個私有的空間,占用內存空間很少。CPU同一時間,只會執行一個線程中的指令。JVM多線程會輪流切換并分配CPU執行時間的方式。為了線程切換后,需要通過程序計數器,來回復正確的執行位置。
學習java,學習jvm,了解JVM運行時數據區,這些就夠了,下面【執行引擎】,【本地庫接口】,【本地方法庫】都是根據不同的操作系統,不同的平臺,做了JVM的適配,例如:linux,windows的執行引擎,本地庫接口都有一些的不同,JVM的目的就是一處編寫導出的運行,作為開發人員掌握在執行引擎之上。
⑥ 線程獨占
每個線程都會有它獨立的空間,隨著生命周期而創建和消亡?!咎摂M機棧,本地方法棧,程序計數器?!?。
⑦ 線程共享
所有線程能訪問這塊內存數據,隨著虛擬機或者GC而創建和消亡。【方法區,堆】。
對象就是放入了堆中,也就是線程共享的。
(三)查看class文件內容
① 找個例子
public class Demo1{
public static void main(String[] args){
int x = 600;
int y = 100;
int a = x / y;
int b = 60;
System.out.println(a + b);
}
}
②執行下面的命令
使用Demo1.java進行測試,編譯成class,完整的javap命令的解析結果
javac Demo1.java
// javap查看內容,說出Demo1.class所有的信息, 【>】意思是輸入到一個Demo1.txt文件
javap -v Demo1.class > Demo1.txt
java -version
③ 版本號、訪問控制
flags: ACC PUBLIC,ACC SUPER
④ 常量池
這個常量池指的類里面包含的靜態常量,編譯這個類需要用到的常量,類的名稱類信息里面也是個常量,類本身需要用到的常量。
⑤ 構造方法
之前類并沒有定義對應的構造方法,但是通過javap之后內部存在一個無參的構造方法。由此可見,沒有定義構造函數時,會有隱式的無參構造函數。
⑥ 程序入口main方法
描述了方法的:訪問控制,本地變量的數目,參數的數量,方法對應棧幀中操作的數棧的深度,JVM執行引擎去執行這些代碼編譯過后的指令碼,javap翻譯出來的操作符,class文件內存儲的是指令碼,前面的數字,是偏移量,jvm根據這個去區分不同的指令。工具叫【JVM指令碼表】進行查閱查看具體指令的含義。
(三)程序完整運行分析
① 編譯加載到方法區
編譯加載到方法區,最后加載Demo1,其實一個JVM運行不止是一個Demo1,涉及到很多很多的類,會將所有的類信息存放到方法區里面,運行的一些常量會放在常量池里面,1.7和之前稱為永久代,1.8開始稱為元數據空間。
② 類加載進去,創建對象運行
類已經加載進去了,需要創建一個對象來進行運行,運行代碼JVM創建線程來執行這些代碼,一定是創建線程,需要配合【虛擬機棧】和【程序計數器】分配響應的空間,這里不涉及到本地代碼因為咱們都是在JVM里面,Thread有一個獨占的空間,其他區域有其他線程占領,【程序計數器】對應了字節碼指令的地址。
③ main方法
線程獨占空間,【程序計數器】標注當前這個線程執行到得位置記錄下來有對應的序號,虛擬機棧里面開辟了一個空間,一個棧有多個棧幀組成,方法對應的一些操作,線程就是取一個或者多個,其實線程就是對應了一個或者多個棧幀,main方法的入口,也就是程序的入口,main方法棧幀包含本地變量表,操作數棧,Demo1 里面一共有5個變量,老鐵可能問不是4個嗎,哪里來了5個,因為main方法里面的String[] args也是一個啊。
解析方法,看不懂對照【JVM指令碼表】
0: sipush 600 #將600這個數值壓入到操作數棧,棧從下往上
3: istore_1 #將操作棧頂保存到本地變量表1,移除操作棧
4: bipush 100 #將 600這個數值保存后,將100放入操作數棧
6: istore_2 #操作數棧棧頂100 保存到本地變量表2上。
7: iload_1 # 讀取本地變量1,壓入操作數棧1
8: iload_2 # 讀取本地變量2,壓入操作數棧,它變成位置1了,前一個操作數棧位置變成2了
9: idiv # 將棧頂兩int類型數相除,結果入棧600/100 = 6,原來操作數棧里面的100,600都移除。
10: istore_3 # 操作數棧棧頂6,保存到本地變量表3上。
11: bipush 60 #將 60這個數值保存后,將60放入操作數棧。
13: istore 4 # 將60放入操作數棧,放入本地變量表4的位置上。
15: getstatic #2 # 取貨類或者接口字段的值并將其推入操作數棧,#2對應常量中,#2放入棧頂。
18: iload_3 #將本地變量3去取出壓入操作數棧
19: iload 4 # 將本地變量4取出來壓入操作數棧
21: iadd # 將棧頂兩個int類型數相加,結果入棧。
22: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
# 調用靜態方法,新的一個方法,那個main方法不跑了,jvm會根據這個方法的描述,創建新棧幀,方法和操作從操作數棧中彈出來,壓入虛擬機棧,然后虛擬機會開始執行虛擬機棧最上邊的棧幀,執行完畢后,再繼續執行main方法對應的棧幀。
25: return # void函數返回,main方法執行結果。
其實java的操作就是對于本地變量表,操作數棧,線程表里面的信息,操作,實現程序想要的效果,一定會要對照【JVM指令碼表】來看一定點分析幾個,java的套路你就了解了。
PS:本次將JVM運行的核心邏輯進行了詳細的解析,JVM運行原理中更底層實現,針對不同的操作系統或者處理器,會有不同的實現,說了運行時數據區,講到了棧,指令碼的執行過程。這也是JAVA能夠實現【一定編寫,處處運行】的原因。下次說下Java線程。
總結
以上是生活随笔為你收集整理的java能够运行的原理_JAVA程序运行原理分析(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java监控网卡_VC++监控网卡状态
- 下一篇: java命令行选项6_6.jdk命令行工