Java是一種面向?qū)ο蟮模o態(tài)類型的苛谷,需要編譯執(zhí)行的語言。運(yùn)行在Java虛擬機(jī)上瓢湃,并提供了自動(dòng)的垃圾回收機(jī)制赫蛇。
編程語言跨平臺的方式:
源代碼跨平臺:
二進(jìn)制跨平臺:
字節(jié)碼落蝙,類加載器,虛擬機(jī)的關(guān)系:
Java將源代碼移迫,通過javac編譯器將代碼編譯成字節(jié)碼.class文件厨埋,在執(zhí)行java命令捐顷,將.class文件加載到虛擬機(jī)中迅涮,通過虛擬機(jī)的類加載器加載成類的實(shí)例,并保存在內(nèi)存中唉地,供運(yùn)行時(shí)使用传透。
字節(jié)碼(Java bytecode)實(shí)際上是由單字節(jié)(byte)的指令組成旷祸,理論上最多支持256個(gè)操作碼(opcode)托享,JVM虛擬機(jī)像計(jì)算機(jī)一樣按字節(jié)碼指令去執(zhí)行它。
- 棧操作指令赃绊,包括與局部變量交互的指令
- 程序流程控制指令
- 對象操作指令羡榴,包括方法調(diào)用指令
- 算術(shù)運(yùn)算以及類型轉(zhuǎn)換指令
算術(shù)操作與類型轉(zhuǎn)換操作碼:
方法調(diào)用指令:
- invokestatic忠售,顧名思義迄沫,這個(gè)指令用于調(diào)用某個(gè)類的靜態(tài)方法,這是方法調(diào)用指令中最 快的一個(gè)泰佳。
- invokespecial, 用來調(diào)用構(gòu)造函數(shù),但也可以用于調(diào)用同一個(gè)類中的 private 方法, 以及 可見的超類方法浇坐。
- invokevirtual近刘,如果是具體類型的目標(biāo)對象宁昭,invokevirtual 用于調(diào)用公共积仗,受保護(hù)和 package 級的私有方法蜕猫。
- invokeinterface,當(dāng)通過接口引用來調(diào)用方法時(shí)隆圆,將會(huì)編譯為 invokeinterface 指令渺氧。
- invokedynamic蹬屹,JDK7 新增加的指令,是實(shí)現(xiàn)“動(dòng)態(tài)類型語言”(Dynamically Typed Language)支持而進(jìn)行的升級改進(jìn)贩耐,同時(shí)也是 JDK8 以后支持 lambda 表達(dá)式的實(shí)現(xiàn)基 礎(chǔ)厦取。
由于Java虛擬機(jī)是基于字節(jié)碼指令執(zhí)行的,所以理論上Java虛擬機(jī)提供了語言無關(guān)性的能力铡买,他所能提供的語言描述能力也要比Java語言本身更加強(qiáng)大寻狂。
如何生成字節(jié)碼蛇券?
通過javac指令,編譯源代碼塘慕,在通過javap -c指令查看字節(jié)碼图呢。
所有的計(jì)算都是在棧上蛤织,但是我們變量的名字和變量的值指蚜,都在本地變量表里涨椒。
JVM是模擬一臺基于棧的計(jì)算機(jī),每個(gè)線程都有獨(dú)屬于自己的線程棧(JVM Stack)免猾,用于存儲棧幀(Frame)猎提。每個(gè)方法調(diào)用旁蔼,JVM都會(huì)自動(dòng)創(chuàng)建一個(gè)棧幀牌芋。棧幀由操作數(shù)棧(Operand Stack),局部變量數(shù)組(Local variables)以及一個(gè)Class引用組成肯夏。Class引用指向當(dāng)前方法在運(yùn)行時(shí)常量池中對應(yīng)的Class驯击。
Demo例子:
public class Demo{
public static void foo(){
int a = 1;
int b = 2;
int c = (a + b) * 5;
}
}
Demo的反編譯文件信息: java -c -v Demo
Classfile /Users/kuaikan/Demo.class
Last modified 2021-2-17; size 249 bytes
MD5 checksum 23df48a1e96791ecfac2821b402e530e
Compiled from "Demo.java"
public class Demo
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #3.#11 // java/lang/Object."<init>":()V
#2 = Class #12 // Demo
#3 = Class #13 // java/lang/Object
#4 = Utf8 <init>
#5 = Utf8 ()V
#6 = Utf8 Code
#7 = Utf8 LineNumberTable
#8 = Utf8 foo
#9 = Utf8 SourceFile
#10 = Utf8 Demo.java
#11 = NameAndType #4:#5 // "<init>":()V
#12 = Utf8 Demo
#13 = Utf8 java/lang/Object
{
public Demo();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0
public static void foo();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=0
0: iconst_1
1: istore_0
2: iconst_2
3: istore_1
4: iload_0
5: iload_1
6: iadd
7: iconst_5
8: imul
9: istore_2
10: return
LineNumberTable:
line 3: 0
line 4: 2
line 5: 4
line 6: 10
}
代碼動(dòng)態(tài)執(zhí)行的例子:
通過上面的gif圖可以看出,foo方法只需要用到椣窘茫空間為2,局部變量表為3槽奕,正好對應(yīng)反編譯出來的房轿,foo方法中的Code部分囱持,stack=2纷妆,locals=3這里。
所以我們可以看出问欠,代碼在編譯之后粒蜈,實(shí)際上已經(jīng)可以確定當(dāng)前方法的椏莶溃空間大小以及局部變量表大小度硝。
從助記符到二進(jìn)制:
在.class文件中都是大量的二進(jìn)制code寿冕,javap相當(dāng)于將二進(jìn)制文件翻譯成可讀的形式輸出出來驼唱。