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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > php >内容正文

php

php编译成二进制文件_JVM字节码文件概述

發布時間:2025/3/20 php 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 php编译成二进制文件_JVM字节码文件概述 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

字節碼文件概述

字節碼文件的跨平臺性

Java語言:跨平臺的語言

  • 當Java源代碼成功編譯字節碼后,如果想在不同的平臺上面運行,則無需再次編譯

  • 這個優勢目前來說已經不再吸引人,因為Python、PHP、Ruby、Lisp等有強大的解釋器

  • 跨平臺已經快成為一門語言的必選特性

Java虛擬機:跨語言的平臺

Java虛擬機不和包括Java在內的任何語言綁定,它只與Class文件這種特定的二進制文件所關聯,無論使用何種語言進行軟件開發,只要能將源文件編譯為正確的Class文件,那么這種語言就可以這Java虛擬機上執行。

JVM的平臺無關性

想要讓一個Java程序正確的運行在JVM中,Java源碼就必須要被編譯為符合JVM規范的字節碼。

  • 前端編譯器的主要任務就是負責將符合Java語法規范的Java代碼轉換為符合JVM規范的字節碼文件

  • javac是一種能夠將Java源碼編譯為字節碼的前端編譯器

  • javac編譯器這將Java源碼編譯為一個有效的字節碼文件過程中經歷了4個步驟,分別是詞法解析、語法解析、語義解析以及生成字節碼

JVM結構

Java的編譯

理解執行引擎
  • 前端編譯

    Java源代碼的編譯結果是字節碼,那么肯定要有一種能夠將Java源代碼編譯為字節碼,承擔這個責任的就是一配置在path環境變量中的javac編譯器。javac是一種能夠將Java源碼編譯為字節碼的前端編譯器。

    優點:

  • 許多Java語法新特性(泛型、內部類等),是靠前端編譯器實現的,而不是依賴虛擬機。

  • 編譯成的Class文件可以直接給JVM解釋器解釋執行,省去編譯時間,加快啟動速度。

    缺點:

  • 對代碼運行效率幾乎沒有任何優化措施。

  • 解釋執行效率較低,所以需要結合下面的JIT編譯。

  • 后端編譯/JIT編譯

    通過Java虛擬機(JVM)內置的即時編譯器(Just In Time Compiler,JIT編譯器);在運行時把Class文件字節碼編譯成本地機器碼的過程。

    優點:

  • 通過在運行時收集監控信息,把"熱點代碼"(Hot Spot Code)編譯成與本地平臺相關的機器碼,并進行各種層次的優化。

  • 可以大大提高執行效率。

    缺點:

  • 收集監控信息影響程序運行。

  • 編譯過程占用程序運行時間。

  • 編譯機器碼占用內存。

  • 靜態提前編譯(AOT)

    程序運行前,直接把Java源碼文件編譯成本地機器碼的過程。

    優點:

  • 編譯不占用運行時間,可以做一些較耗時的優化,并可加快程序啟動。

  • 把編譯的本地機器碼保存磁盤,不占用內存,并可多次使用。

    缺點:

  • 因為Java語言的動態性(如反射)帶來了額外的復雜性,影響了靜態編譯代碼的質量,一般靜態編譯不如JIT編譯的質量,這種方式用得比較少。

  • 目前Java體系中主要還是采用前端編譯+JIT編譯的方式

    運作過程:

  • 首先通過前端編譯把符合Java語言規范的程序代碼轉化為滿足JVM規范所要求Class格式。

  • 然后程序啟動時Class格式文件發揮作用,解釋執行,省去編譯時間,加快啟動速度。

  • 針對Class解釋執行效率低的問題,在運行中收集性能監控信息,得知"熱點代碼"。

  • JIT逐漸發揮作用,把越來越多的熱點代碼"編譯優化成本地代碼,提高執行效率。

  • 透過字節碼指令看代碼細節

    面試題

  • 類文件結構有幾個部分?

  • 知道字節碼嗎?字節碼都有哪些?Integer x = 5; int y = 5; 比較 x == y 都經歷哪些步驟?

  • image

    首先在聲明Integer x的時候會調用Integer.valueOf方法,首先看下這個方法

    public?static?Integer?valueOf(int?i)?{
    ????????if?(i?>=?IntegerCache.low?&&?i?<=?IntegerCache.high)
    ????????????return?IntegerCache.cache[i?+?(-IntegerCache.low)];
    ????????return?new?Integer(i);
    }

    如果所傳入的值是-128~127之間,就只用靜態內部類的cache數組,否則就new一個新的對象。

    這也表明為什么 Integer x = 128; Integer y = 128; x != y 的原因。

    之后可以看到調用了 Integer.intValue 進行自動拆箱操作,使得 x 變成 int 類型進行比較。

    虛擬機的基石:Class文件

    • 字節碼文件里是什么?

      源代碼經過編譯器編譯之后便生成一個字節碼文件,字節碼是一種二進制的類文件,它的內容是JVM的指令,而不像C、C++經由編譯器直接生成機器碼。

    • 什么是字節碼指令?

      Java虛擬機的指令,由一個字節長度的、代表著某種特定操作含義的操作碼(opcode),以及跟隨其后的零至多個代表此操作所需參數的操作數(operand)所構成。虛擬機中許多指令并不包含操作數,只有一個操作碼。

      比如 aload_1 (操作碼) 、aload 4 (操作碼 + 操作數),因為aload只有0、1、2、3所以如果想繼續擴充,就要用到操作數。

      image

    Class文件結構

    • Class類的本質

      由于一個Class文件都對應著唯一一個類或接口的定義信息,但反過來說,Class文件實際上它并不一定以磁盤文件形式存在。Class文件是一組以8位字節為基礎單位的二進制流。

    • Class文件格式

      Class的結構不像 XML 等描述語言,由于它沒有任何分割符號,所以這其中的數據項,無論是字節順序還是數量,都是被嚴格限定的,哪個字節代表什么含義、長度多少、先后順序,都不允許改變。

      Class文件格式采用一種類似于C語言結構體的方式進行數據存儲,這種結構中只有兩種數據類型:無符號數和表。

    • 無符號數屬于基本數據類型,以 u1 、u2 、u4、u8 來分別代表1個字節、2個字節、4個字節和8個字節的無符號數,無符號數可以用來描述數字、索引引用、數量值或者按照UTF-8編碼構成字符串值。

    • 表是由多個無符號數或者其它表作為數據項構成的復合數據類型,所有表都習慣性地以"_info"結尾。表用于描述有層次關系的復合結構的數據,整個Class文件本質就是一張表,由于表沒有固定長度,所以通常會其前面加上個數說明。

    • Class文件結構概述

      Class文件的結構并不是一成不變的,隨著Java虛擬機的不斷發展,總是不可避免地會對Class文件結構做出一些調整,但是基本結構和框架是非常穩定的。

      結構如下:

    • 魔數

    • Class文件版本

    • 常量池

    • 訪問標志

    • 類索引,父類索引,接口索引集合

    • 字段表集合

    • 方法表集合

    • 屬性表集合

      image類型名稱說明長度數量
      u4magic魔數,識別Class文件格式4個字節1
      u2minor_version副版本號(小版本)2個字節1
      u2major_version主版本號(大版本)2個字節1
      u2constant_pool_count常量池計數器2個字節1
      cp_infoconstant_pool常量池表n個字節constant_pool_count-1
      u2access_flags訪問標識2個字節1
      u2this_class類索引2個字節1
      u2super_class父類索引2個字節1
      u2interfaces_count接口計數器2個字節1
      u2interfaces接口索引集合2個字節interfaces_count
      u2fields_count字段計數器2個字節1
      field_infofields字段表n個字節fields_count
      u2methods_count方法計數器2個字節1
      method_infomethods方法表n個字節methods_count
      u2attributes_count屬性計數器2個字節1
      attribute_infoattributes屬性表n個字節attributes_count

    字節碼文件解析

    首先我們創建一個簡單的源碼:

    public?class?Demo?{
    ????private?int?num?=?1;

    ????public?int?add()?{
    ????????num?=?num?+?2;
    ????????return?num;
    ????}
    }

    通過 javac 命令編譯后可以看到Class文件內容:

    public?class?Demo?{
    ????private?int?num?=?1;

    ????public?Demo()?{
    ????}

    ????public?int?add()?{
    ????????this.num?+=?2;
    ????????return?this.num;
    ????}
    }

    為什么編譯以后增加了無參構造器,以及this關鍵字,我們一點點分析。

    之后我們使用Notepad++,需要安裝一個HEX-Editor插件來打開這個Class文件就可以看到下圖內容。

    image

    這里就直接使用 excel 來清晰解釋具體內容。

    image

    魔數:Class文件的標志

    • 每個Class文件開頭的4個字節的無符號整數成為魔數(Magic Number)

    • 它的唯一作用就是確定這個文件是否為一個能被虛擬機接受的有效合法的Class文件。即:魔數是Class文件的標識符。

    • 魔數值固定為0xCAFEBABE

    • 如果一個Class文件不以0xCAFEBABE開頭,虛擬機在進行文件校驗的時候就會直接拋出ClassFormatError錯誤。

    • 使用魔數而不是擴展名來識別主要是基于安全方面考慮,因為文件擴展名是可以隨意改變的。

      Class文件版本號

    • 緊接著魔數的 4 個字節是 Class文件的版本號。同樣也是4個字節。第5個和第6個字節所代表的的含義就是編譯的副版本號minor_version,而第7個和第8個字節就是編譯的主版本號major_version。

    • 它們共同構成了Class文件的格式版本號。譬如某個Class文件的主版本號為M,副版本號為m,那么這個Class文件的格式版本號就是 M.m。

    • 版本號和Java編譯器的對應關系表如下:

      主版本(十進制)副版本(十進制)編譯器版本
      4531.1
      4601.2
      4701.3
      4801.4
      4901.5
      5001.6
      5101.7
      5201.8
      5301.9
      5401.10
      5501.11
    • Java的版本號是從45開始的,JDK 1.1 之后的每個JDK大版本發布主版本號向上加1。

    • 不同版本的Java編譯器編譯的Class文件對應的版本是不一樣的。目前,高版本的Java虛擬機可以執行低版本編譯器編譯的Class文件,但是低版本的Java虛擬機不能執行由高版本編譯器生成的Class文件。否則JVM會拋出java.lang.UnsupportedClassVersionError異常。

    常量池:存放所有常量

    • 常量池是Class文件中內容最為豐富的區域之一。常量池對于Class文件中的字段和方法解析有著至關重要的作用。

    • 隨著Java虛擬機的不斷發展,常量池的內容也日漸豐富。可以說,常量池是整個Class文件的基石。

    • 在版本號之后,緊跟著的就是常量池的數量,以及若干個常量池表項。

    • 常量池中常量的數量是不固定的,所以需要一個u2類型的無符號數,代表著常量池容量計數器(constant_pool_count),與Java語言習慣不一樣的是,容量計數器是從1開始而不是0。

    • 常量池表項中,用于存放編譯期生成的各種字面量和符號引用,這部分內容將在類加載后進入方法區的運行時常量池中存放。

    常量池計數器

    • 由于常量池的數量不固定,所以需要放置兩個字節來表示常量池容量計數器。

    • 常量池計數器(u2類型):從1開始,表示常量池中有多少項常量。即constant_pool_count = 1表示常量池中有0個常量池。

    • 我們看剛才舉例的Demo:其值為0x0016,轉換為十進制也就是22。

      image

      但是實際中只有21項常量,范圍是1-21。

    這里的常量池把第0項空出來了,為了滿足后面某些指向常量池的索引值的數據在特定情況下需要表達"不引用任何一個常量池項"的含義,這種情況可以使用索引0來表示。

    常量池表

    • constant_pool是一種表及結構,以1 ~ constant_pool_count - 1 為索引。表明后面有多少個常量項。

    • 常量池主要存放放兩大類變量:字面量(Literal)和符號引用(Symbolic Reference)。

    • 它包含了Class文件結構及其子結構中引用的所有字符串常量、類、或接口名、字段名、和其它常量。常量池中的每一項都具有相同特征。第1個字節作為類型標記,用于確定該項的格式,這個字節成為tag byte (標記字節)。

    字面量和符號引用

    常量池主要存放放兩大類變量:字面量(Literal)和符號引用(Symbolic Reference)。

    常量具體的常量
    字面量文本字符串
    聲明為final的常量值
    符號引用類和接口的全限定名
    字段和名稱的描述符
    方法的名稱和描述符

    全限定名

    com/test/Demo這個就是類的全限定名,僅僅是把包名的"."替換成了"/",為了使連續的多個全限定名之間不產生混淆,在使用時最后一般會加入一個";"表示全限定名結束。

    簡單名稱

    簡單名稱是指沒有類型和參數修飾的方法或者字段名稱,上面例子中的類的add()方法,和num字段的簡單名稱分別是add和num。

    描述符

    描述符的作用是用來描述字段的數據類型、方法的參數列表(包括數量、類型以及順序)和返回值。根據描述符規則,基本數據類型(boolean,byte,char,short,int,float,long,double)以及代表無返回值的void類型都用一個大寫字符來表示,而對象類型則用字符L加對象的全限定名來表示:

    標志符含義
    B基本數據類型byte
    C基本數據類型char
    D基本數據類型double
    F基本數據類型float
    I基本數據類型int
    J基本數據類型long
    S基本數據類型short
    Z基本數據類型boolean
    V代表void類型
    L對象類型,比如:Ljava/lang/Object;
    [數組類型,代表一維數組。比如:double[][][] = [[[D
    public?static?void?main(String[]?args)?{
    ????????Object[]?arr?=?new?Object[10];
    ????????System.out.println(arr);//[Ljava.lang.Object;@14ae5a5

    ????????Long[][]?longs?=?new?Long[10][10];
    ????????System.out.println(longs);//[[Ljava.lang.Long;@7f31245a

    ????????int[][]?ints?=?new?int[10][10];
    ????????System.out.println(ints);//[[I@7f31245a
    }

    需要注意的是,用描述符來描述方法的時候,先參數列表后返回值的順序描述,參數列表按照參數的嚴格順序放在一組小括號"()"內。

    虛擬機在加載Class文件時才會進行動態鏈接,也就是說,Class文件中不會保存各個方法和字段的最終內存布局信息,因此,這些字段和方法的符號引用不經過轉換是無法直接被虛擬機使用的。當虛擬機運行時,需要從常量池中獲得對應的符號引用,再在類加載過程中的解析階段將其替換為直接引用,并翻譯到具體的內存地址中。

    • 符號引用:符號引用以一組符號來描述所引用的目標,符號可以是任何形式的字面量,只要使用時無歧義地定位到目標即可。符號引用與虛擬機實現的內存布局無關,引用的目標并不一定已經加載到了內存中。

    • 直接引用:直接引用可以是直接指向目標的指針、相對偏移量或是一個能間接定位到目標的句柄。直接引用是與虛擬機實現的內存布局相關的,同一個符號引用在不同虛擬機實例上翻譯出來一般不會相同。如果有了直接引用,則說明引用的目標必定存在內存之中。

    常量類型和結構

    類型標志(或標識)描述
    CONSTANT_utf8_info1UTF-8編碼的字符串
    CONSTANT_Integer_info3整型字面量
    CONSTANT_Float_info4浮點型字面量
    CONSTANT_Long_info5長整型字面量
    CONSTANT_Double_info6雙精度浮點型字面量
    CONSTANT_Class_info7類或接口的符號引用
    CONSTANT_String_info8字符串類型字面量
    CONSTANT_Fieldref_info9字段的符號引用
    CONSTANT_Methodref_info10類中方法的符號引用
    CONSTANT_InterfaceMethodref_info11接口中方法的符號引用
    CONSTANT_NameAndType_info12字段或方法的符號引用
    CONSTANT_MethodHandle_info15表示方法句柄
    CONSTANT_MethodType_info16標志方法類型
    CONSTANT_InvokeDynamic_info18表示一個動態方法調用點
    image

    從上面的圖中可以看到,雖然每一項的結構都不相同,但是它們有個共同點,就是每一項的第一個字節都是一個標志位,標識這一項是哪種類型的常量。

    訪問標記(access_flag)

    • 在常量池后,緊接著訪問標記,該標記使用兩個字節表示,用于識別一些類或者接口層次的訪問信息,包括:這個Class是類還是接口;是否定義為 public 類型;是否定義為 abstract 類型;如果是類的話,是否被聲明為 final 等,詳細說明如下:

      標志名稱標志值含義
      ACC_PUBLIC0x0001標志為public類型
      ACC_FINAL0x0010標志被聲明為final,只有類可以設置
      ACC_SUPER0x0020標志允許使用invokespecial字節碼指令的新語義,JDK1.0.2之后編譯出來的類的這個標志默認為真。(使用增強的方法調用父類方法)
      ACC_INTERFACE0x0200標志這是一個接口
      ACC_ABSTRACT0x0400是否為abstract類型,對于接口或者抽象類來說,次標志值為真,其他類型為假
      ACC_SYNTHETIC0x1000標志此類并非由用戶代碼產生(即:由編譯器產生的類,沒有源碼對應)
      ACC_ANNOTATION0x2000標志這是一個注解
      ACC_ENUM0x4000標志這是一個枚舉
    • 類的訪問權限通常為 ACC_ 開頭的常量。

    • 每一種類型的表示都是通過設置訪問標記的32位中的特定位來實現的,比如如果是public final的類,則標記為 ACC_PUBLIC | ACC_FINAL。

    • 使用ACC_SUPER可以讓類更準確的定位到父類的方法super.method(),默認都是設置并使用這個標記。

    我們可以看到上面的Demo的字節碼對應的訪問標記是21,也就是對應表格中的 ACC_PUBLIC 和 ACC_SUPER 加起來就等于21。

    類索引、父類索引、接口索引集合

    長度含義
    u2this_class
    u2super_class
    u2interfaces_count
    u2interfaces[interfaces_count]
    • 類索引用于確定這個類的繼承關系。

    • 父類索引用于確定這個類的父類全限定名,由于Java語言不允許多重繼承,所以父類索引只有一個,除了Object 之外,所有的Java類都有父類,因此除了Object之外,所有Java類的父類索引都不為0。

    • 接口索引集合就用來描述這個類實現了拿些接口,這些被實現的接口將按implements語句(如果當前類本身是一個接口,則應當是 extends 語句)后的接口順序從左到右排列在接口索引集合中。

    this_class(類索引)

    2 字節無符號整數,指向常量池的索引。它提供了類的全限定名,如com/test/Demo。this_class的值必須是常量池中某項的一個有效索引值。常量池在這個索引出的成員必須為constant_class_info類型結構體,該結構表示這個Class文件所定義的類或者接口。

    super_class(父類索引)

    • 2字節無符號整數,指向常量池的索引。它提供了當前類的父類全限定名。如果我們沒有繼承任何類,其默認繼承的是Java/lang/Object類。同時,由于Java不支持多繼承,所以其父類只有一個。

    • super_class指向的父類不能是final類型。

    interfaces

    • 指向常量池索引集合,它提供了一個符號引用到所有已實現的接口。

    • 由于一個類可以實現多個接口,因此需要以數組形式保存多個接口的索引,表示接口的每個索引也是一個指向常量池的Constant_Class(指向的是接口,并不是類)。

    interfaces_count(接口計數器)

    interfaces_count 項的值表示當前類或者接口的直接超接口數量。

    interfaces[](接口索引集合)

    interfaces[]中每個成員的值必須是對常量池表中某項索引的有效索引值,它的長度為interfaces_count。每個成員interfaces[i]必須為constant_class_info結構,其中 0 <= i < interfaces_count。在interfaces[]中,各成員所表示的接口順序對應的源代碼中給定的接口順序(從左至右)一樣,即 interface[0]對應的是源代碼中最左邊的接口。

    字段表集合

    • 用于描述接口或類中聲明的變量。字段(field)包括類級變量以及實例變量,但是不包括方法內部、代碼塊內部聲明的局部變量。

    • 字段叫什么名字,字段被定義為什么數據類型,這些都是無法固定的。只能引用常量池中的常量來描述。

    • 它指向常量池索引集合,它描述了每個字段的完整信息。比如字段的標識符、訪問修飾符(public、private或protected)、是類變量還是實例變量(static修飾符)、是否是常量(final修飾符)等。

    注意:

    • 字段表集合中不會列出從父類或者實現的接口中繼承來的字段,但有可能列出原本Java代碼之中不存在的字段。譬如在內部類中為了保持對外部類的訪問性,會自動添加指向外部類實例的字段。

    • 在Java語言中字段是無法重載的,兩個字段的數據類型,修飾符不管是否相同,都必須使用不一樣的名稱,但是對于字節碼來說,如果兩個字段的描述符不一致,那字段名重名也是合法的。

    fields_count(字段計數器)

    fields_count的值表示當前Class文件fields表的成員個數,用2個字節表示。

    fields表中每個成員都是一個field_info結構,用于表示該類或接口所聲明的所有字段或者實例字段,不包括方法內部聲明的變量,也不包括從父類或父接口繼承的那些字段。

    fields[](字段表)

    • fields表中的每個成員都必須是一個field_info結構的數據項,用于表示當前類或接口中某個字段的完整描述。

    • 一個字段的信息包括如下這些信息。這些信息中,各個修飾符都是布爾值,要么有,要么沒有。

    • 作用域

    • 實例變量還是類變量

    • 是否final

    • 是否volatile

    • 是否序列化 transient 修飾

    • 字段數據類型(基本數據類型,對象,數組)

    • 字段名稱

    • 字段表結構

    類型名稱含義數量
    u2access_flags訪問標志1
    u2name_index字段名索引1
    u2descriptor_index描述符索引1
    u2attributes_count屬性計數器1
    attribute_infoattributes屬性集合attributes_count

    字段表訪問標識

    我們知道,一個字段可以被各種關鍵字修飾,比如作用域修飾符、static修飾符、final修飾符、volatile修飾符等等。字段訪問標志有如下這些:

    標志名稱標志值含義
    ACC_PUBLIC0x0001字段是否為public
    ACC_PRIVATE0x0002字段是否為private
    ACC_PROTECTED0x0004字段是否為protected
    ACC_STATIC0x0008字段是否為static
    ACC_FINAL0x0010字段是否為final
    ACC_VOLATILE0x0040字段是否為volatile
    ACC_TRANSTENT0x0080字段是否為transient
    ACC_SYNCHETIC0x1000字段是否為由編譯器自動產生
    ACC_ENUM0x4000字段是否為enum

    字段名索引

    根據字段名索引的值,查詢常量池中的指定索引項即可。

    描述符索引

    描述符的作用是用來描述字段的數據類型、方法的參數列表(包括數量、類型以及順序)和返回值。根據描述符規則,基本數據類型及無返回值的void都是用大寫符來表示,對象使用字符L+全限定名表示。

    屬性集合

    一個字段還可能擁有一些屬性,用于存儲更多的額外信息。比如初始化值、一些注釋信息等。屬性個數存放在attribute_count中,屬性具體內容存在attributes數組中。

    結構為:

    ConstantValue_attribute{
    ????u2?attribute_name_index;
    ????u4?attribute_length;
    ????u2?constantvalue_index;
    }

    對于常量屬性而言,attribute_length的值恒為2。

    image

    常量值索引所指向的 #8 其實就是對應int的值。

    image

    方法表集合

    methods:指向常量池索引集合,它完整描述了每個方法的簽名。

    • 在字節碼文件中,每一個method_info項都對應著一個類或者接口中的方法信息。比如方法的訪問修飾符,方法的返回值類型,以及方法的參數信息等。

    • 如果方法不是抽象的或者不是native的,那么字節碼就會體現出來。

    • 一方面,methods表只描述當前類或接口中聲明的方法,不包括從父類或父接口繼承的方法。另一方面,methods表有可能會出現由編譯器自動添加的方法,最典型的就是編譯器產生的方法信息,比如類或者接口初始化方法()和實例初始化方法()。

    methodds_count(方法計數器)

    methods_count的值表示當前class文件methods表的成員個數。使用 2 個字節來表示。

    methods表中每個成員都是一個method_info結構。

    methods[](方法表)

    • methods表中的每個成員都必須是一個method_info結構,用于表示當前類或接口中某個方法的完整描述。如果某個method_info結構的access_flags項既沒有設置ACC_NATIVE標志和ACC_ABSTRACT標志,那么該結構中也應該包含實現這個方法所用到的Java虛擬機指令。

    • method_info結構可以表示類和接口中定義的所有方法,包括實例方法、類方法、實例初始化方法和類或接口初始化方法。

    • 方法表的結構實際和字段表是一致的。如下:

    類型名稱含義數量
    u2access_flags訪問標志1
    u2name_index字段名索引1
    u2descriptor_index描述符索引1
    u2attributes_count屬性計數器1
    attribute_infoattributes屬性集合attributes_count

    屬性表集合

    方法表集合之后的屬性表集合,指的是class文件所攜帶的輔助信息,比如該Class文件的源文件的名稱,以及任何帶有RetentionPolicy.CLASS或者RetentionPolicy.RUNTIME的注解。這類信息通常被用于Java虛擬機的驗證和運行,以及Java程序的調試。

    此外,字段表、方法表都可以有自己的屬性表。用于描述某些場景專有的信息。

    屬性表集合的限制沒有那么嚴格,不再要求各個屬性表具有嚴格的順序,并且只要不與已有的屬性名重復,任何人實現的編譯器都可以向屬性表中寫入自己定義的屬性信息,但Java虛擬機運行時會忽略掉它不認識的屬性。

    屬性的通用格式

    屬性表的結構比較靈活,各種不同的屬性只要滿足以下結構即可:

    類型名稱數量含義
    u2attribute_name_index1屬性名索引
    u4attribute_length1屬性長度
    u1infoattribute_length屬性表

    屬性類型

    屬性名稱使用位置含義
    Code方法表Java代碼編譯成的字節碼指令
    ConstantValue字段表final關鍵字定義的常量池
    Deprecated類,方法,字段表被聲明為deprecated的方法和字段
    Exceptions方法表方法拋出的異常
    EnclosingMethod類文件僅當一個類為局部類或者匿名類是才能擁有這個屬性,這個屬性用于標識這個類所在的外圍方法
    InnerClass類文件內部類列表
    LineNumberTableCode屬性Java源碼的行號與字節碼指令的對應關系
    LocalVariableTableCode屬性方法的局部變量描述
    StackMapTableCode屬性JDK1.6中新增的屬性,供新的類型檢查檢驗器檢查和處理目標方法的局部變量和操作數有所需要的類是否匹配
    Signature類,方法表,字段表用于支持泛型情況下的方法簽名
    SourceFile類文件記錄源文件名稱
    SourceDebugExtension類文件用于存儲額外的調試信息
    Synthetic類,方法表,字段表標志方法或字段為編譯器自動生成的
    LocalVariableTypeTable使用特征簽名代替描述符,是為了引入泛型語法之后能描述泛型參數化類型而添加
    RuntimeVisibleAnnotations類,方法表,字段表為動態注解提供支持
    RuntimeInvisibleAnnotations表,方法表,字段表用于指明哪些注解是運行時不可見的
    RuntimeVisibleParameterAnnotation方法表作用與RuntimeVisibleAnnotations屬性類似,只不過作用對象為方法
    RuntimeInvisibleParameterAnnotation方法表作用與RuntimeInvisibleAnnotations屬性類似,作用對象哪個為方法參數
    AnnotationDefault方法表用于記錄注解類元素的默認值
    BootstrapMethods類文件用于保存invokeddynamic指令引用的引導方式限定符

    Code屬性

    Code屬性就是存放方法體里面的代碼,像接口或者抽象方法,沒有具體的方法體,因此也就不會有Code屬性了。

    Code屬性表的結構:

    類型名稱數量含義
    u2attribute_name_index1屬性名索引
    u4attribute_length1屬性長度
    u2max_stack1操作數棧深度的最大值
    u2max_locals1局部變量表所需的存續空間
    u4code_length1字節碼指令的長度
    u1codecode_length存儲字節碼指令
    u2exception_table_length1異常表長度
    exception_infoexception_tableexception_length異常表
    u2attributes_count1屬性集合計數器
    attribute_infoattributesattributes_count屬性集合

    LineNumberTable屬性

    • LineNumberTable屬性是可選變長屬性,位于Code結構的屬性表

    • LineNumberTable屬性是用來描述Java源碼行號與字節碼行號之間的對應關系。

    • start_pc,即字節碼行號;line_number,即Java源代碼的行號

    • 在Code屬性的屬性表中,LineNumberTable屬性可以按照任意順序出現,此外,多個LineNumberTable屬性可以共同表示一個行號在源文件中表示的內容,即LineNumberTable屬性不需要與源文件的行一一對應。

    LineNumberTable屬性表結構

    LineNumberTable_attribute?{??
    ???u2?attribute_name_index;??
    ???u4?attribute_length;??
    ???u2?line_number_table_length;??
    ???{???u2?start_pc;??
    ???????u2?line_number;??
    ???}?line_number_table[line_number_table_length];??
    }?
    類型名稱數量含義
    u2attribute_name_index1屬性名索引
    u4attribute_length1屬性長度
    u2line_number_table_length1行號表長度
    line_number_infoline_number_tableline_number_table_length行號表

    SourceFile屬性

    類型名稱數量含義
    u2attribute_name_index1屬性名索引
    u4attribute_length1屬性長度
    u2sourcefile_index1源碼文件索引

    其長度總是固定的8個字節。

    image

    我們看之前的Excel最后一位也可以看到對應的就是源文件名。

    總結

    以上是生活随笔為你收集整理的php编译成二进制文件_JVM字节码文件概述的全部內容,希望文章能夠幫你解決所遇到的問題。

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