亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

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

Eminjannn / 3049人閱讀

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

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

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

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

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

JVM并非只為Java設(shè)計(jì),而字節(jié)碼也并非只有Java才可以編譯得到,早在Java發(fā)展之初,設(shè)計(jì)者便將Java規(guī)范拆分為Java語言規(guī)范Java虛擬機(jī)規(guī)范,同時(shí)也承諾,對JVM做適當(dāng)?shù)臄U(kuò)展,以便更好地支持其他語言運(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的來源是何種語言,在JVM發(fā)展到1.7~1.8的時(shí)候,設(shè)計(jì)者通過JSR-292基本兌現(xiàn)了以上承諾

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

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

接下來,均會以這段代碼為示例進(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ù)

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

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

版本號

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

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

常量池

緊接著主次版本號之后的是常量池入口

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

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

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

訪問標(biāo)志

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

訪問標(biāo)志用于識別這個(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)識這個(gè)類并非由用戶代碼生成
ACC_ANNOTATION 標(biāo)識這是一個(gè)注解
ACC_ENUM 標(biāo)識這是一個(gè)枚舉
類索引、父類索引、接口索引集合

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

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

字段表集合

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

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

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

描述符

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

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

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

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

屬性表集合

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

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

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

附: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)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/74750.html

相關(guān)文章

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

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

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

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

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

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

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

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

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

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

    sanyang 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<