日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java虚拟机编译_[四] java虚拟机JVM编译器编译代码简介 字节码指令实例 代码到底编译成了什么形式...

發布時間:2023/12/2 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java虚拟机编译_[四] java虚拟机JVM编译器编译代码简介 字节码指令实例 代码到底编译成了什么形式... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言簡介

前文已經對虛擬機進行過了簡單的介紹,并且也對class文件結構,以及字節碼指令進行了詳盡的說明

想要了解JVM的運行機制,以及如何優化你的代碼,你還需要了解一下,java編譯器到底是如何編譯你的代碼的

本文不是從最底層的編譯原理講解

本文是針對java代碼,去查看歸納總結編譯器的結果行為,從而直觀的感受到字節碼指令集

也就是說本文的內容,主要針對的是使用javap 查看字節碼文件中方法的code屬性中的字節碼內容

讓你從java代碼? class文件格式,以及字節碼指令集 進行一個直觀的演示

提醒:

如果你對字節碼指令不了解,而且,沒有看過前面的文章,本文可能會輕度不適.

本文示例只是為了展示

您應該經常查看你自己的代碼的class文件去發現其中的規律

一條普通的指令格式

[ [ ... ]] []

index 表示偏移量 行號? 等

opcode 表示操作碼

operandX表示操作數

comment 為注釋

比如下圖所示

行號

0 , 操作碼 getstatic ,操作數 #24? 注釋為 Field

java/lang/System..................

其中 index? 行號/偏移量? 可以作為控制跳轉指令的跳轉目標? 比如 goto? 8

表示跳轉到索引為8的指令上

還有一點需要注意的是,javap查看到的內容,你可以認為是class文件表述的信息,但是絕不能理解為就是class文件中的內容

比如,class文件中沒有操作碼的助記符,比如,getstatic

,都是指令的二進制值

再比如剛才說到的,跳轉到指定行號,對于控制轉移指令,實際的操作數是在當前指令的操作碼集合中的地址偏移量

并不是那個8

只不過javap工具按照更適合我們閱讀的方式進行了翻譯

加載存儲與算數指令

public static voidmain(String[] args) {int i = -1;int j = 3;int k = 5;int l = 127;int m = 32767;int n = 32768;int add = i+j;int sub = i-j;int mul = j*k;int div = j/k;int rem = k%j;int neg = ~j;int inc = i++;

}

-1 ~ 5

使用const加載到操作數棧 其中-1 使用iconst_m1

-128~127 使用bipush

-32768~32767使用sipush

其余常量池ldc

store從操作數棧保存到局部變量表

load加載局部變量到操作數棧

0. 常量-1 加載到操作數棧

1. 操作數棧保存到1號局部變量表 也就是 i = -1;

2. 常量 3 加載到操作數棧

3. 操作數棧保存到2號局部變量表 也就是j = 3;

4. 常量 5

加載到操作數棧

5. 操作數棧保存到3號局部變量表 也就是k =5;

6. 常量 127

加載到操作數棧

8. 操作數棧保存到4號局部變量表 也就是l = 127;

10.常量 32767 加載到操作數棧

13.操作數棧保存到5號局部變量表 也就是m =

32767;

15.加載#17號常量池數據到操作數棧

17. 操作數棧保存到6號局部變量表 也就是n = 32768;

19. 加載1號局部變量到操作數棧 對應 i

20.

加載2號局部變量到操作數棧? 對應 j

21. 執行iadd指令計算并將結果壓入棧頂?? 對應 i+j;

22.

保存棧頂元素到7號局部變量

24. 加載1號局部變量到操作數棧 對應 i

25. 加載2號局部變量到操作數棧? 對應

j

26.執行isub指令計算并將結果壓入棧頂?? 對應i-j;

27. 保存棧頂元素減法結果到8號局部變量

29,30

加載 2號和3號局部變量到操作數棧 也就是j?? k

31? 執行imul指令并將結果壓棧 j*k

32

保存棧頂元素乘法結果到9號局部變量

34.35 加載 2號和3號局部變量到操作數棧 也就是j?? k

36 執行idiv

結果壓入棧頂

37保存idiv結果到10號局部變量

39.40 加載3號 和 2號 也就是k?? j

41 執行求余irem

結果壓入棧頂

42 棧頂元素結果保存到11號局部變量

44加載2號局部變量? 對應 j 到操作數棧

45

加載常量-1到操作數棧

46 執行異或運算結果壓入棧頂? (~x = -1 ^ x;)

47棧頂結果保存到12號局部變量

49

加載1號局部變量 對應 i

50 執行增量 1 計算 結果壓入棧頂

53 棧頂結果保存到13號變量

55 void方法

return返回

類型轉換指令

public static voidmain(String[] args) {boolean bNum = true;char cNum = 2;byte byteNum = 127;short sNum = 32767;int iNum = 100;long lNum = 65536;float fNum = 2.5f;double dNum = 6.8;char c1 = (char)byteNum;char c2 = (char)sNum;char c3 = (char)iNum;char c4 = (char)lNum;char c5 = (char)fNum;char c6 = (char)dNum;byte b1 = (byte)cNum;byte b2 = (byte)sNum;byte b3 = (byte)iNum;byte b4 = (byte)lNum;byte b5 = (byte)fNum;byte b6 = (byte)dNum;short s1 = (short)cNum;short s2 = (short)byteNum;short s3 = (short)iNum;short s4 = (short)lNum;short s5 = (short)fNum;short s6 = (short)dNum;int i1 = (int)cNum;int i2 = (int)byteNum;int i3 = (int)sNum;int i4 = (int)lNum;int i5 = (int)fNum;int i6 = (int)dNum;long l1 = (long)byteNum;long l2 = (long)cNum;long l3 = (long)sNum;long l4 = (long)iNum;long l5 = (long)fNum;long l6 = (long)dNum;float f1 = (float)byteNum;float f2 = (float)cNum;float f3 = (float)sNum;float f4 = (float)iNum;float f5 = (float)lNum;float f6 = (float)dNum;double d1 = (double)byteNum;double d2 = (double)cNum;double d3 = (double)sNum;double d4 = (double)iNum;double d5 = (double)lNum;double d6 = (double)fNum;

}

javap解析后的內容太長,接下來分段解析

數據的加載與存儲

從數據的存儲可以看得出來

boolean內部使用的是數值1? 也就是1 表示true

數據類型轉換為char類型

char byte short int? 內部形式均為int? 所以轉換為char是,使用的全都是 i2c

long? float double 先轉換為int(l2i f2i d2i) 然后在統一使用

i2c 轉換為char

數據類型轉換為byte 類型

char byte short int? 內部形式均為int? 所以轉換為byte時,使用的全都是 i2b

long? float double 先轉換為int(l2i f2i

d2i) 然后在統一使用 i2b 轉換為? byte

數據類型轉換為short 類型

還是同樣的道理,char byte short int? 內部形式均為int? 所以轉換為short

使用的是?? i2s

long? float double 先轉換為int(l2i f2i

d2i) 然后在統一使用 i2s 轉換為? short

數據類型轉換為int 類型

char byte

short內部都是int類型.將他們轉換為int時,不需要進行轉換

如下圖所示,一個load 對應一個store

long? float double??? (l2i

f2i d2i)?? 轉換為int

數據類型轉換為long 類型

char byte short? int?? 內部都是int類型.將他們轉換為long

時,使用? i2l

float double?? 轉換為long?? f2l d2l

數據類型轉換為float 類型

char byte short? int?? 內部都是int類型.將他們轉換為float 時,使用? i2f

long double?? 轉換為float???? l2f? d2f

數據類型轉換為double 類型

char byte short? int?? 內部都是int類型.將他們轉換為double 時,使用? i2d

long

float?? 轉換為double???? l2d? f2d

類相關指令

classSuper{

}class Sub extendsSuper{

}newObject();newSuper();

Super s= newSuper();new Double(1.5);newSub();

Sub sub= new Sub();

new Object();

new Super();

沒有賦值給局部變量 僅僅是創建對象? 調用new之后,堆中對象的引用保存在棧頂

然后調用構造方法invokespecial

Super s = new Super();

同上面的,需要調用new

因為還需要保存到局部變量

所以new之后 先copy一個,也就是dup

然后調用構造方法 invokespecial

然后從操作數棧保存到局部變量 store

Super super1 = newSuper();

Sub sub= newSub();//父類引用可以指向子類//子類引用不能指向父類//但是對于指向子類的父類引用 可以通過類型轉換為子類Super subToSuper=sub;

Sub superToSub= (Sub) subToSuper;

0

創建Spper

3 復制

4 調用構造方法

7 保存到1號局部變量

8 創建Sub

11 復制

12調用構造方法

15

保存到2號局部變量

16 2號加載到操作數棧

17保存到3號局部變量

18加載3號局部變量到棧

19 checkcast

進行校驗確認是否可以轉換為指定類型 否則報錯拋 classCastException

22 再次保存到局部變量

控制轉移指令

voidintWhile() {int i = 0;while (i < 100) {

i++;

}

}voidintDoWhile() {int i = 0;do{

i++;

}while (i < 100);

}voidintFor() {int j = 0;for(int i =0;i<100;i++) {

j++;

}

}

intWhile()方法

0.

加載常量0 到操作數棧

1.保存操作數棧元素到1號局部變量 i= 0;

2.直接跳轉到第8行

8.1號局部變量加載到操作數棧 也就是i

作為第一個元素

9.加載常量100到操作數棧 也就是100作為第二個元素

11.比較大小,如果前者小于后者 也就是如果 i <100 滿足

跳轉到第5行? 否則順序執行到14 return

5.給1號局部變量以增量1 增加

然后

8-->9-->11-->5-->8-->9-->11......往復循環 直到條件不滿足,從11 跳轉到14

結束

intDoWhile()

0.加載常量0到操作數棧

1.保存常量0

到1號局部變量

2.給1號局部變量以增量1 進行自增

5.1號局部變量加載到操作數棧

6.常量100加載到操作數棧

8,比較大小

如果前者小于后者也就是 1號局部變量 i<100 跳轉到第2行

然后進行往復循環,直到條件不滿足,然后順序到return

intFor()

0.? 加載常量0 到操作數棧

1.? 保存棧頂元素到1號局部變量 j=0;

2.? 加載常量0到操作數棧

3.? 保存棧頂元素到2號局部變量i=0;

4.? 跳轉到13行

13.? 加載2號局部變量到操作數棧

14.? 加載常量100到操作數棧

16.? 比較大小,如果前者 2號局部變量 i <100

跳轉到7

7.? 1號局部變量以增量1? 自增 j++

10.?? 2號局部變量以增量1 自增

i++

13.? 2號局部變量加載到操作數棧

14.? 加載常量100到操作數棧

16.? 比較大小,如果前者 2號局部變量 i <100 跳轉到7

往復循環 如果條件不滿足 從16 順序到19 結束方法 return

public voidfun() {int i = 0;if(i<2) {

i++;

}else{

i--;

}

}

0, 加載常量0

到棧頂

1,保存棧頂元素 (0) 到1號局部變量

2. 加載1號局部變量到棧頂

3. 加載常量2

到棧頂

4,比較

如果大于后者等于跳轉到13 然后1號局部變量 自增1 然后下一步順序到16 return

否則就是順序執行到7 1號局部變量

增量為-1? 自增運算 然后到10 ,10為跳轉到16 return

方法調用相關指令

public voidinvoker() {

method(2);

}public void method(inti) {if(i>5) {

System.out.println(i);

}

}

invoker()

0,加載0號

局變量到棧?? (上面基本都是第一個數據被保存到1號局部變量,0 號其實是被this 占用了)

1,加載常量2

到操作數棧

2.調用實例方法(I)V

5 return

method(int)

0. 加載1號局部變量到操作數棧

1.

加載常量5 到操作數棧

2比較如果小于等于 跳轉到12行 直接返回

如果大于

那么順序執行到5行?????? out 是類型為PrintStream的?? System中的靜態變量

8

加載1號局部變量到操作數棧

9 調用實例方法 println? 是? PrintStream的實例方法

使用invokevirtual

switch 相關

int i = 5;int j = 6;switch(i) {case 1:

j= j + 1;break;case 3:

j= j + 2;break;case 5:

j= j + 3;break;default:

j= j + 4;

}

0,1,2,4

分別將 5 和 6 加載并存儲到1號和2號局部變量

5.加載1號局部變量到棧? 對應 switch (i)

{

然后根據tableswitch 表 進行跳轉

雖然我們只有1,3,5? 但是設置了1到5 ,對于2 和 4

直接跳轉到default

40: 2號局部變量 +1

順序到43

43: 跳轉到61 return

46: 2號局部變量 +2

順序到49

49: 跳轉到61 return

52: 2號局部變量 +3

順序到55

55: 跳轉到61 return

58 2號局部變量 +4

順序到61

return

int j = 6;

String string= "hehe";switch(string) {case "A":

j= j + 1;break;case "hehe":

j= j + 2;break;case "C":

j= j + 3;break;default:

j= j + 4;

}

0? 加載常量6到棧

1 保存到 1 號局部變量

3.加載常量池 #36 到棧

5 保存到2 號局部變量

6 加載2號局部變量

到棧

7 復制棧頂元素

8 復制的元素保存到3號局部變量

9 調用String實例化方法hashCode

12,

lookupswitch表中,不在類似tableswitch 了,那個是連續的

lookupswitch? 是不連續的

我們總共有三個case一個default

lookupswitch 總共有4項

"A" 的hashCode? 為 65

"C" 的hashCode為 67

"hehe" 的hashCode為 3198650? 不信的話,自己寫個main方法打印下

經過12行

路由之后跳轉到指定的序列

你會發現三個case他們的過程是一樣的

加載3號局部變量 ,然后將常量 A C? hehe

也加載到棧

然后調用equal方法進行比較

代碼千千萬,本文只是找一些基本的示例展示字節碼與代碼的對應關系,想要熟悉這塊

唯有沒事多javap看看你代碼的class文件,才能通宵領悟,進而更好地優化你的代碼

比如看看下面的一個很典型的例子

int i = 5;int j = 8;int k = i+j;int l = 3+6;

前一部分:

0.

常量5 加載到棧

1,保存到 1號局部變量

2. 常量8 加載到棧

4 保存到2號 局部變量

5,加載1號局部變量

6,

加載2號局部變量

7 執行iadd 結果會壓入棧頂

8 棧頂元素保存到3號局部變量

至此

完成了前三行代碼

后一部分:

9.常量9 加載到棧?? (3+6? 已經被計算好了)

11,保存到4號局部變量

總結

以上是生活随笔為你收集整理的java虚拟机编译_[四] java虚拟机JVM编译器编译代码简介 字节码指令实例 代码到底编译成了什么形式...的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。