一、基礎概念
Java位元組碼是Java虛擬機(JVM)可以執行的指令集,它是Java語言跨平台的秘密之一。
Java源代碼在編譯後將變成Java位元組碼。Java位元組碼以「.class」文件格式存在於文件系統中。運行Java程序時,Java虛擬機(JVM)將Java位元組碼文件加載並解釋為機器碼。這種解釋方式使得Java位元組碼可以在任何計算機體系結構上運行。
Java位元組碼是一種高度優化的指令集,它包含眾多的指令來實現不同的功能。Java位元組碼是緊湊的,可以減少程序的存儲空間。在將Java源代碼編譯成Java位元組碼時,編譯器會對代碼進行優化,使得生成的位元組碼更加緊湊,提高程序的執行效率。
二、Java位元組碼的結構
Java位元組碼由指令、操作數和操作數棧組成。每個Java位元組碼指令都有一個或多個操作數。Java虛擬機會將這些操作數從操作數棧中取出,並對它們進行相應的操作。Java位元組碼指令可以操縱任何類型的數據,包括數值、引用和對象。
Java位元組碼有兩種類型:面向棧和面向本地變量。面向棧指令將操作數從操作數棧中取出並操作。面向本地變量指令將數據從本地變量表中取出並操作。在Java位元組碼中,使用單位元組、雙位元組、三位元組或四位元組的指令表示不同的操作。
Java位元組碼包含以下組件:
- 魔數和版本號
- 常量池
- 訪問標誌
- 類索引、超類索引和接口索引
- 字段信息
- 方法信息
- 屬性信息
三、Java位元組碼的優化
Java位元組碼在編譯時通常是經過了一定程度的優化的。Java編譯器(javac)會對程序進行優化,使得生成的Java位元組碼更加緊湊,提高程序的執行效率。可以在Java編譯時使用「-O」選項來啟用優化。
除了編譯時優化,Java虛擬機(JVM)也可以在運行時對Java位元組碼進行優化。JVM可以使用即時編譯器(JIT)將位元組碼編譯成本地機器碼,並進行一些優化。這種技術被稱為JIT優化,可以提高程序的運行效率。
四、Java位元組碼實例演示
下面是一個簡單的Java類:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
該類在編譯後會生成「.class」文件,並包含Java位元組碼。我們可以使用Java反編譯器(如「javap」命令)將位元組碼反編譯成類似於Java源代碼的格式。以下是反編譯的結果:
public class HelloWorld {
public HelloWorld();
public static void main(java.lang.String[]);
static {};
}
從反編譯的結果可以看出,該類包含一個默認構造函數、一個主函數和一個靜態代碼塊。
以下是生成的Java位元組碼:
Classfile /HelloWorld.class
Last modified 2021-10-08; size 227 bytes
MD5 checksum 327ad7f7e8cf27c237f9ecc7d41f1045
Compiled from "HelloWorld.java"
public class HelloWorld
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #5.#19 // java/lang/Object."":()V
#2 = Fieldref #20.#21 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #22 // Hello, World!
#4 = Methodref #23.#24 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #25 // java/lang/Object
#6 = Utf8
#7 = Utf8 ()V
#8 = Utf8 Code
#9 = Utf8 LineNumberTable
#10 = Utf8 main
#11 = Utf8 ([Ljava/lang/String;)V
#12 = Utf8 SourceFile
#13 = Utf8 HelloWorld.java
#14 = Utf8 InnerClasses
#15 = Utf8 BootstrapMethods
#16 = Methodref #17.#18 // HelloWorld.lambda$main$0:()V
#17 = Class #34 // HelloWorld
#18 = NameAndType #35:#36 // lambda$main$0:()V
#19 = NameAndType #6:#7 // "":()V
#20 = Class #37 // java/lang/System
#21 = NameAndType #38:#39 // out:Ljava/io/PrintStream;
#22 = Utf8 Hello, World!
#23 = Class #40 // java/io/PrintStream
#24 = NameAndType #41:#42 // println:(Ljava/lang/String;)V
#25 = Utf8 java/lang/Object
#26 = Utf8 java/lang/System
#27 = Utf8 java/io/PrintStream
#28 = Utf8 java/lang/String
#29 = Utf8 BootstrapMethods
#30 = MethodHandle #6:#43 // invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#31 = MethodType #7 // ()V
#32 = InvokeDynamic #0:#35 // InvokeDynamic #0:lambda:()V
#33 = Utf8 HelloWorld
#34 = Utf8 HelloWorld
#35 = Utf8 lambda
#36 = Utf8 ()V
#37 = Utf8 java/lang/System
#38 = Utf8 out
#39 = Utf8 Ljava/io/PrintStream;
#40 = Utf8 java/io/PrintStream
#41 = Utf8 println
#42 = Utf8 (Ljava/lang/String;)V
#43 = Methodref #44.#45 // java/lang/invoke/MethodHandles.lookup:()Ljava/lang/invoke/MethodHandles$Lookup;
#44 = Class #46 // java/lang/invoke/MethodHandles
#45 = NameAndType #47:#48 // lookup:()Ljava/lang/invoke/MethodHandles$Lookup;
#46 = Utf8 java/lang/invoke/MethodHandles
#47 = Utf8 lookup
#48 = Utf8 ()Ljava/lang/invoke/MethodHandles$Lookup;
{
public HelloWorld();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
LineNumberTable:
line 1: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LHelloWorld;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Hello, World!
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: aload_0
9: invokedynamic #32, 0 // InvokeDynamic #0:lambda:()V
14: return
LineNumberTable:
line 3: 0
line 4: 8
line 3: 14
LocalVariableTable:
Start Length Slot Name Signature
0 15 0 args [Ljava/lang/String;
}
SourceFile: "HelloWorld.java"
InnerClasses:
#5; //class java/lang/Object
BootstrapMethods:
0: #30 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#31 ()V
invokestatic HelloWorld.lambda$main$0:()V
#31 ()V
從Java位元組碼可以看出,該類包含一個默認構造函數「HelloWorld()」和一個主函數「main(String[])」。在主函數中,先輸出字符串「Hello, World!」,然後調用了一個lambda表達式。在Java位元組碼中,lambda表達式會被編譯成一個帶有「invokedynamic」指令的方法。這個方法使用LambdaMetafactory工廠方法動態創建一個函數,以代替lambda表達式的執行。
原創文章,作者:UQROZ,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/333147.html
微信掃一掃
支付寶掃一掃