成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

【修煉內(nèi)功】[JVM] 類文件結(jié)構(gòu)

Eminjannn / 2991人閱讀

摘要:本文已收錄修煉內(nèi)功躍遷之路學(xué)習(xí)語(yǔ)言的時(shí)候,需要在不同的目標(biāo)操作系統(tǒng)上或者使用交叉編譯環(huán)境,使用正確的指令集編譯成對(duì)應(yīng)操作系統(tǒng)可運(yùn)行的執(zhí)行文件,才可以在相應(yīng)的系統(tǒng)上運(yùn)行,如果使用操作系統(tǒng)差異性的庫(kù)或者接口,還需要針對(duì)不同的系統(tǒng)做不同的處理宏的

本文已收錄【修煉內(nèi)功】躍遷之路

學(xué)習(xí)C語(yǔ)言的時(shí)候,需要在不同的目標(biāo)操作系統(tǒng)上(或者使用交叉編譯環(huán)境),(使用正確的CPU指令集)編譯成對(duì)應(yīng)操作系統(tǒng)可運(yùn)行的執(zhí)行文件,才可以在相應(yīng)的系統(tǒng)上運(yùn)行,如果使用操作系統(tǒng)差異性的庫(kù)或者接口,還需要針對(duì)不同的系統(tǒng)做不同的處理(宏)

Java的出現(xiàn)也正是為了解決"平臺(tái)無關(guān)性","Write Once, Run Anywhere"的口號(hào)也充分表達(dá)了軟件開發(fā)人員對(duì)沖破平臺(tái)接線的渴求

"與平臺(tái)無關(guān)"的最終實(shí)現(xiàn)還是要在操作系統(tǒng)的應(yīng)用層上,這便是JVM的存在,不同的平臺(tái)有不同的JVM,而所有的JVM都可以載入與平臺(tái)無關(guān)的字節(jié)碼,從而實(shí)現(xiàn)程序的"一次編寫,到處運(yùn)行"

JVM并非只為Java設(shè)計(jì),而字節(jié)碼也并非只有Java才可以編譯得到,早在Java發(fā)展之初,設(shè)計(jì)者便將Java規(guī)范拆分為Java語(yǔ)言規(guī)范Java虛擬機(jī)規(guī)范,同時(shí)也承諾,對(duì)JVM做適當(dāng)?shù)臄U(kuò)展,以便更好地支持其他語(yǔ)言運(yùn)行于JVM之上,而這一切的基礎(chǔ)便是Class文件(字節(jié)碼文件),Class文件中存放了JVM可以理解運(yùn)行的字節(jié)碼命令

In the future, we will consider bounded extensions to th Java virtual machine to provide better support for other languages

JVM并不關(guān)心Class的來源是何種語(yǔ)言,在JVM發(fā)展到1.7~1.8的時(shí)候,設(shè)計(jì)者通過JSR-292基本兌現(xiàn)了以上承諾

本篇不會(huì)去詳細(xì)地介紹如何去解析Class文件,目的是為了了解Class文件的結(jié)構(gòu),Class文件中都包含哪些內(nèi)容

Class文件可以由JVM加載并執(zhí)行,其中記錄了類信息、變量信息、方法信息、字節(jié)碼指令等等,雖然JVM加載Class之后(在JIT之前)進(jìn)行的是解釋執(zhí)行,但Class文件并不是文本文件,而是被嚴(yán)格定義的二進(jìn)制流文件

接下來,均會(huì)以這段代碼為示例進(jìn)行分析

import java.io.Serializable;

public class ClassStruct implements Serializable {
    private static final String HELLO = "hello";
    private String name;

    public ClassStruct(String name) {
        this.name = name;
    }

    public void print() {
        System.out.println(HELLO + ": " + name);
    }

    public static void main(String[] args) {
        ClassStruct classStruct = new ClassStruct("ManerFan");
        classStruct.print();
    }
}

使用$ javac ClassStruct.java進(jìn)行編譯,編譯后的文件可以使用$ javap -p -v ClassStruct查看Class文件的內(nèi)容(見文章末尾)

魔數(shù)

很多文件存儲(chǔ)都會(huì)使用魔數(shù)來進(jìn)行身份識(shí)別,比如圖片文件,即使將圖片文件改為不正確的后綴,絕大多數(shù)圖片預(yù)覽器也會(huì)正確解析

同樣Class文件也不例外,使用二進(jìn)制模式打開Class文件,會(huì)發(fā)現(xiàn)所有Class文件的前四個(gè)字節(jié)均為OxCAFEBABE,這個(gè)魔術(shù)在Java還被稱為"Oak"語(yǔ)言的時(shí)候就已經(jīng)確定下來了

版本號(hào)

緊接著魔術(shù)的四個(gè)字節(jié)(接下來不再對(duì)照二進(jìn)制進(jìn)行查看,而是直接查看javap幫我們解析出來的結(jié)果,見文章末尾)存儲(chǔ)的是Class文件的版本號(hào),前兩個(gè)字節(jié)為次版本號(hào)(minor version),后兩個(gè)字節(jié)為主版本號(hào)(major version)

Java版本號(hào)從45開始,高版本的JDK可以向下兼容低版本的Class文件,但無法向上兼容高版本,即使文件格式并未發(fā)生變化

常量池

緊接著主次版本號(hào)之后的是常量池入口

常量池中主要存放兩大類常量:字面量(Literal)和符號(hào)引用(Symbolic Reference)

字面量:如文本字符串、被聲明為final的常量值等
符號(hào)引用:類和接口的全限定名(Fully Qualified Name)、字段的名稱和描述(Descriptor)、方法的名稱和描述符

Java代碼在進(jìn)行編譯時(shí),并不像C或C++那樣有"連接"這一步驟,而是在虛擬機(jī)加載Class文件時(shí)進(jìn)行動(dòng)態(tài)連接,Class文件中不會(huì)保存各方法和字段的內(nèi)存布局,在虛擬機(jī)運(yùn)行時(shí),需要從常量池中獲得對(duì)應(yīng)的符號(hào)引用,再在類創(chuàng)建或運(yùn)行時(shí)解析并翻譯到具體的內(nèi)存地址中,才能被虛擬機(jī)使用

訪問標(biāo)志

訪問標(biāo)志用于識(shí)別一些類或接口層次的訪問信息

訪問標(biāo)志用于識(shí)別這個(gè)Class是類還是接口;是否定義為public;是否為abstract類型;是否聲明為final;等等,具體標(biāo)志含義如下

標(biāo)志 名稱
ACC_PUBLIC 是否為public類型
ACC_FINAL 是否被聲明為final
ACC_SUPER 是否允許使用invokespecial字節(jié)碼指令
ACC_INTERFACE 是否為接口
ACC_ABSTRACT 是否為abstract
ACC_SYNTHETIC 標(biāo)識(shí)這個(gè)類并非由用戶代碼生成
ACC_ANNOTATION 標(biāo)識(shí)這是一個(gè)注解
ACC_ENUM 標(biāo)識(shí)這是一個(gè)枚舉
類索引、父類索引、接口索引集合

Class文件中由類索引(this_class)、父類索引(super_class)及接口索引集合(interfaces)三項(xiàng)數(shù)據(jù)確定這個(gè)類的繼承關(guān)系

父類索引只有一個(gè)(對(duì)應(yīng)extends語(yǔ)句),而接口索引則是一個(gè)集合(對(duì)應(yīng)implements語(yǔ)句)

字段表集合

字段表(field_info)用于描述類或者接口中聲明的變量

字段(field)包括了類級(jí)變量(如static)及實(shí)例級(jí)變量,但不包括在方法內(nèi)部聲明的變量

字段包含的信息有:作用域(public、private、protected)、類級(jí)還是實(shí)例級(jí)(static)、可變性(final)、并發(fā)可見性(volatile)、可否序列化(transient)、數(shù)據(jù)類型、字段名等

描述符

這里簡(jiǎn)單解釋一下描述符(descriptor)

描述符用來描述字段數(shù)據(jù)類型、方法參數(shù)列表和返回值,根據(jù)描述符規(guī)則,基本數(shù)據(jù)類型及代表無返回值的void類型都用一個(gè)大寫字符表示,對(duì)象類型則用字符L加對(duì)象全限定名來表示

標(biāo)識(shí)字符 含義
B byte
C char
D double
F float
I int
J long
S short
Z boolean
V void
L 對(duì)象類型,如 Ljava/lang/Object;

對(duì)于數(shù)組,每一個(gè)維度使用一個(gè)前置的[來描述,如java.lang.String[][]將被記錄為[[java/lang/String;int[]將被記錄為[I

描述方法時(shí),按照先參數(shù)列表,后返回值的順序描述,參數(shù)列表放在()內(nèi),如void inc()描述符為()V,方法java.lang.String toString()描述符為()Ljava/lang/String;,方法int indexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset, int targetCount, int fromIndex的描述符為([CII[CIII)I

方法表集合

Class文件中對(duì)方法的描述與對(duì)字段的描述幾乎采用了完全一致的方式,方法表的結(jié)構(gòu)依次包括了訪問標(biāo)志(access_flags)、名稱索引(name_index)、描述符索引(descriptor_index)、屬性表集合(attributes),但是方法內(nèi)部的代碼并不在方法表中,而是經(jīng)過編譯器編譯成字節(jié)碼指令后,存放在屬性表集合中一個(gè)名為"Code"的屬性中

屬性表集合

在Class文件、字段表、方法表中都可以攜帶自己的屬性表集合,用于描述某些場(chǎng)景專有的信息,Java虛擬機(jī)規(guī)范中預(yù)定義了9種虛擬機(jī)實(shí)現(xiàn)應(yīng)當(dāng)能識(shí)別的屬性

屬性名稱 使用位置 含義
Code 方法表 Java代碼編譯成的字節(jié)碼指令
ConstantValue 字段表 final關(guān)鍵自定義的常量值
Deprecated 類、方法表、字段表 被聲明為deprecated的方法和字段
Exceptions 方法表 方法拋出的異常
InnerClasses 類文件 內(nèi)部類列表
LineNumberTable Code屬性 Java源碼的行號(hào)與字節(jié)碼指令的對(duì)應(yīng)關(guān)系
LocalVariableTable Code屬性 方法的局部變量描述
SourceFile 類文件 原文件名稱
Synthetic 類、方法表、字段表 標(biāo)識(shí)方法或字段為編譯器自動(dòng)生成的

關(guān)于屬性表,會(huì)在之后的文章中穿插介紹

附:Class文件
Classfile ~/articles/【修煉內(nèi)功】躍遷之路/JVM/[JVM] 類文件結(jié)構(gòu)/src/ClassStruct.class
  Last modified 2019-6-2; size 829 bytes
  MD5 checksum 9f7454acd0455837a33ff8e03edffdb3
  Compiled from "ClassStruct.java"
public class ClassStruct implements java.io.Serializable
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #14.#31        // java/lang/Object."":()V
   #2 = Fieldref           #6.#32         // ClassStruct.name:Ljava/lang/String;
   #3 = Fieldref           #33.#34        // java/lang/System.out:Ljava/io/PrintStream;
   #4 = Class              #35            // java/lang/StringBuilder
   #5 = Methodref          #4.#31         // java/lang/StringBuilder."":()V
   #6 = Class              #36            // ClassStruct
   #7 = String             #37            // hello:
   #8 = Methodref          #4.#38         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #9 = Methodref          #4.#39         // java/lang/StringBuilder.toString:()Ljava/lang/String;
  #10 = Methodref          #40.#41        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #11 = String             #42            // ManerFan
  #12 = Methodref          #6.#43         // ClassStruct."":(Ljava/lang/String;)V
  #13 = Methodref          #6.#44         // ClassStruct.print:()V
  #14 = Class              #45            // java/lang/Object
  #15 = Class              #46            // java/io/Serializable
  #16 = Utf8               HELLO
  #17 = Utf8               Ljava/lang/String;
  #18 = Utf8               ConstantValue
  #19 = String             #47            // hello
  #20 = Utf8               name
  #21 = Utf8               
  #22 = Utf8               (Ljava/lang/String;)V
  #23 = Utf8               Code
  #24 = Utf8               LineNumberTable
  #25 = Utf8               print
  #26 = Utf8               ()V
  #27 = Utf8               main
  #28 = Utf8               ([Ljava/lang/String;)V
  #29 = Utf8               SourceFile
  #30 = Utf8               ClassStruct.java
  #31 = NameAndType        #21:#26        // "":()V
  #32 = NameAndType        #20:#17        // name:Ljava/lang/String;
  #33 = Class              #48            // java/lang/System
  #34 = NameAndType        #49:#50        // out:Ljava/io/PrintStream;
  #35 = Utf8               java/lang/StringBuilder
  #36 = Utf8               ClassStruct
  #37 = Utf8               hello:
  #38 = NameAndType        #51:#52        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #39 = NameAndType        #53:#54        // toString:()Ljava/lang/String;
  #40 = Class              #55            // java/io/PrintStream
  #41 = NameAndType        #56:#22        // println:(Ljava/lang/String;)V
  #42 = Utf8               ManerFan
  #43 = NameAndType        #21:#22        // "":(Ljava/lang/String;)V
  #44 = NameAndType        #25:#26        // print:()V
  #45 = Utf8               java/lang/Object
  #46 = Utf8               java/io/Serializable
  #47 = Utf8               hello
  #48 = Utf8               java/lang/System
  #49 = Utf8               out
  #50 = Utf8               Ljava/io/PrintStream;
  #51 = Utf8               append
  #52 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #53 = Utf8               toString
  #54 = Utf8               ()Ljava/lang/String;
  #55 = Utf8               java/io/PrintStream
  #56 = Utf8               println
{
  private static final java.lang.String HELLO;
    descriptor: Ljava/lang/String;
    flags: ACC_PRIVATE, ACC_STATIC, ACC_FINAL
    ConstantValue: String hello

  private java.lang.String name;
    descriptor: Ljava/lang/String;
    flags: ACC_PRIVATE

  public ClassStruct(java.lang.String);
    descriptor: (Ljava/lang/String;)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."":()V
         4: aload_0
         5: aload_1
         6: putfield      #2                  // Field name:Ljava/lang/String;
         9: return
      LineNumberTable:
        line 7: 0
        line 8: 4
        line 9: 9

  public void print();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=1, args_size=1
         0: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: new           #4                  // class java/lang/StringBuilder
         6: dup
         7: invokespecial #5                  // Method java/lang/StringBuilder."":()V
        10: ldc           #7                  // String hello:
        12: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        15: aload_0
        16: getfield      #2                  // Field name:Ljava/lang/String;
        19: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        22: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        25: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        28: return
      LineNumberTable:
        line 12: 0
        line 13: 28

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=2, args_size=1
         0: new           #6                  // class ClassStruct
         3: dup
         4: ldc           #11                 // String ManerFan
         6: invokespecial #12                 // Method "":(Ljava/lang/String;)V
         9: astore_1
        10: aload_1
        11: invokevirtual #13                 // Method print:()V
        14: return
      LineNumberTable:
        line 16: 0
        line 17: 10
        line 18: 14
}
SourceFile: "ClassStruct.java"
參考:
深入理解Java虛擬機(jī)

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/74750.html

相關(guān)文章

  • 修煉內(nèi)功】[JVM] 深入理解JVM之ClassLoader

    摘要:本文已收錄修煉內(nèi)功躍遷之路在誕生之初便提出,各提供商發(fā)布很多不同平臺(tái)的虛擬機(jī),這些虛擬機(jī)都可以載入并執(zhí)行同平臺(tái)無關(guān)的字節(jié)碼。設(shè)計(jì)者在第一版虛擬機(jī)規(guī)范中便承諾,時(shí)至今日,商業(yè)機(jī)構(gòu)和開源機(jī)構(gòu)已在之外發(fā)展出一大批可以在上運(yùn)行的語(yǔ)言,如等。 本文已收錄【修煉內(nèi)功】躍遷之路 Java在誕生之初便提出 Write Once, Run Anywhere,各提供商發(fā)布很多不同平臺(tái)的虛擬機(jī),這些虛擬機(jī)...

    荊兆峰 評(píng)論0 收藏0
  • 修煉內(nèi)功】[JVM] 虛擬機(jī)棧及字節(jié)碼基礎(chǔ)

    摘要:本文已收錄修煉內(nèi)功躍遷之路在淺談虛擬機(jī)內(nèi)存模型一文中有簡(jiǎn)單介紹過,虛擬機(jī)棧是線程私有的,每個(gè)方法在執(zhí)行的同時(shí)都會(huì)創(chuàng)建一個(gè)棧幀,方法執(zhí)行時(shí)棧幀入棧,方法結(jié)束時(shí)棧幀出棧,虛擬機(jī)中棧幀的入棧順序就是方法的調(diào)用順序?qū)懥撕芏辔淖?,但都不盡如意,十分慚 本文已收錄【修煉內(nèi)功】躍遷之路 showImg(https://segmentfault.com/img/bVbtSi5?w=1654&h=96...

    VEIGHTZ 評(píng)論0 收藏0
  • 修煉內(nèi)功】[Java8] Lambda究竟是不是匿名的語(yǔ)法糖

    摘要:本文已收錄修煉內(nèi)功躍遷之路初次接觸的時(shí)候感覺表達(dá)式很神奇表達(dá)式帶來的編程新思路,但又總感覺它就是匿名類或者內(nèi)部類的語(yǔ)法糖而已,只是語(yǔ)法上更為簡(jiǎn)潔罷了,如同以下的代碼匿名類內(nèi)部類編譯后會(huì)產(chǎn)生三個(gè)文件雖然從使用效果來看,與匿名類或者內(nèi)部類有相 本文已收錄【修煉內(nèi)功】躍遷之路 showImg(https://segmentfault.com/img/bVbui4o?w=800&h=600)...

    ?xiaoxiao, 評(píng)論0 收藏0
  • 修煉內(nèi)功】[JVM] 虛擬機(jī)視角的方法調(diào)用

    摘要:本文已收錄修煉內(nèi)功躍遷之路我們寫的方法在被編譯為文件后是如何被虛擬機(jī)執(zhí)行的對(duì)于重寫或者重載的方法,是在編譯階段就確定具體方法的么如果不是,虛擬機(jī)在運(yùn)行時(shí)又是如何確定具體方法的方法調(diào)用不等于方法執(zhí)行,一切方法調(diào)用在文件中都只是常量池中的符號(hào)引 本文已收錄【修煉內(nèi)功】躍遷之路 showImg(https://segmentfault.com/img/bVbuesq?w=2114&h=12...

    shevy 評(píng)論0 收藏0
  • 修煉內(nèi)功】[JVM] 淺談虛擬機(jī)內(nèi)存模型

    摘要:也正是因此,一旦出現(xiàn)內(nèi)存泄漏或溢出問題,如果不了解的內(nèi)存管理原理,那么將會(huì)對(duì)問題的排查帶來極大的困難。 本文已收錄【修煉內(nèi)功】躍遷之路 showImg(https://segmentfault.com/img/bVbsP9I?w=1024&h=580); 不論做技術(shù)還是做業(yè)務(wù),對(duì)于Java開發(fā)人員來講,理解JVM各種原理的重要性不必再多言 對(duì)于C/C++而言,可以輕易地操作任意地址的...

    sanyang 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<