一個(gè)Java程序怎樣運(yùn)行起來(lái)的【class解析全過(guò)程】

      網(wǎng)友投稿 739 2022-05-29

      首先編寫(xiě)一測(cè)試程序

      public class Test {

      public static void main(String[] args){

      System.out.println("HelloWorld");

      }

      }

      執(zhí)行javac Test.java 得到Test.class文件(編譯過(guò)程有點(diǎn)復(fù)雜,這里先不看)

      執(zhí)行java Test,控制臺(tái)輸出"test",想要弄清楚java程序是怎么運(yùn)行起來(lái)首先得了解清楚class文件

      看下Test.class里究竟是什么東西,class文件的內(nèi)容如下:

      上圖中都是以16進(jìn)制表示,接下來(lái)挨個(gè)分析其中的內(nèi)容表示什么意思。class文件中存儲(chǔ)的數(shù)據(jù)可以參考下表:

      1、magic 魔數(shù)

      CA FE BA BE

      魔數(shù),確定該文件是否是虛擬機(jī)可以接受的文件

      2、class文件版本信息

      00 00 00 33

      class文件的版本號(hào),51表示jdk1.7.0

      3、常量池

      3.1常量池入口

      00 1D

      常量池?cái)?shù)量為29-1=28,每個(gè)類(lèi)只有一個(gè)常量池

      常量池中放了字符串,常量值,類(lèi)名稱(chēng),字段名,方法名等,反編譯下Test.class,看看常量池中存放了哪些東西

      Constant pool:

      #1 = Methodref #6.#15 // java/lang/Object."":()V

      #2 = Fieldref #16.#17 // java/lang/System.out:Ljava/io/PrintStream;

      #3 = String #18 // test

      #4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V

      #5 = Class #21 // Test

      #6 = Class #22 // java/lang/Object

      #7 = Utf8

      #8 = Utf8 ()V

      #9 = Utf8 Code

      #10 = Utf8 LineNumberTable

      #11 = Utf8 main

      #12 = Utf8 ([Ljava/lang/String;)V

      #13 = Utf8 SourceFile

      #14 = Utf8 Test.java

      #15 = NameAndType #7:#8 // "":()V

      #16 = Class #23 // java/lang/System

      #17 = NameAndType #24:#25 // out:Ljava/io/PrintStream;

      #18 = Utf8 test

      #19 = Class #26 // java/io/PrintStream

      #20 = NameAndType #27:#28 // println:(Ljava/lang/String;)V

      #21 = Utf8 Test

      #22 = Utf8 java/lang/Object

      #23 = Utf8 java/lang/System

      #24 = Utf8 out

      #25 = Utf8 Ljava/io/PrintStream;

      #26 = Utf8 java/io/PrintStream

      #27 = Utf8 println

      #28 = Utf8 (Ljava/lang/String;)V

      常量池中的項(xiàng)目類(lèi)型有:

      CONSTANT_Utf8_info ?????tag標(biāo)志位為1, ??UTF-8編碼的字符串,比如類(lèi)或接口的全限定名,參數(shù)名等

      CONSTANT_Integer_info ?tag標(biāo)志位為3, int整型字面量

      CONSTANT_Float_info ????tag標(biāo)志位為4, float浮點(diǎn)型字面量

      CONSTANT_Long_info ????tag標(biāo)志位為5, long長(zhǎng)整形字面量

      CONSTANT_Double_info ?tag標(biāo)志位為6, double雙精度字面量

      CONSTANT_Class_info ???tag標(biāo)志位為7, 類(lèi)或接口的符號(hào)引用,指向包含字符串字面值的CONSTANT_Utf8表

      CONSTANT_String_info ???tag標(biāo)志位為8,字符串類(lèi)型的字面量,指向包含字符串字面值的CONSTANT_Utf8表

      CONSTANT_Fieldref_info ?tag標(biāo)志位為9, ?字段的符號(hào)引用,指向包含該字段所屬類(lèi)名的CONSTANT_Utf8表

      CONSTANT_Methodref_info ?tag標(biāo)志位為10,類(lèi)中方法的符號(hào)引用,指向包含該方法所屬類(lèi)型的CONSTANT_Utf8表

      CONSTANT_InterfaceMethodref_info tag標(biāo)志位為11, 接口中方法的符號(hào)引用

      CONSTANT_NameAndType_info ?tag 標(biāo)志位為12,字段和方法的名稱(chēng)以及類(lèi)型的符號(hào)引用

      3.2常量池內(nèi)容

      接上,繼續(xù)分析class中的內(nèi)容,參照 jvm官方文檔 ,看下常量池中究竟是什么東西

      常量池1-----0A 00 06 00 0F ??//

      1,0A---tag為10,表示第一個(gè)常量類(lèi)型為CONSTANT_Methodref,參照官方文檔,CONSTANT_Methodref的結(jié)構(gòu)為

      CONSTANT_Methodref_info {

      u1 tag;

      u2 class_index;

      u2 name_and_type_index;

      }

      所以后面跟了4個(gè)字節(jié)

      2,00 06---聲明當(dāng)前方法類(lèi)描述符索引值為6 ????// ?java/lang/Object

      3,00 0F---當(dāng)前方法的名稱(chēng)和類(lèi)型索引值為15 ?// ?"":()V

      所以,結(jié)合上文中反編譯出的內(nèi)容來(lái)看,這幾個(gè)16進(jìn)制翻譯過(guò)來(lái)正好是

      #1 = Methodref #6.#15 // java/lang/Object."":()V

      常量池2----09 00 10 00 11

      1,09---tag為9,類(lèi)型為CONSTANT_Fieldref

      2,00 10---聲明當(dāng)前方法類(lèi)描述符索引值為16 // java/lang/System

      3,00 11---字段描述符的名稱(chēng)和類(lèi)型索引值為17 // ?out:Ljava/io/PrintStream;

      這幾個(gè)16進(jìn)制翻譯過(guò)來(lái)正好是

      #2 = Fieldref ? ? ? ? ? #16.#17 ? ? ? ?// ?java/lang/System.out:Ljava/io/PrintStream;

      常量池3---08 00 12

      1,08---tag為8,類(lèi)型為CONSTANT_String,根據(jù)官方文檔,其結(jié)構(gòu)為

      CONSTANT_String_info {

      u1 tag;

      u2 string_index;

      }

      所以后面跟了兩個(gè)字節(jié)

      2,00 12---聲明當(dāng)前String值所在的索引值為18

      當(dāng)前16進(jìn)制翻譯過(guò)來(lái),表示

      #3 = String #18 // test

      常量池4---0A ?00 13 00 14,對(duì)照著上面的分析,

      #4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V

      常量池5---07 00 15

      1,07---tag為7,類(lèi)型為CONSTANT_Class,根據(jù)官方文檔,其結(jié)構(gòu)為

      CONSTANT_Class_info {

      u1 tag;

      u2 name_index;

      }

      2,00 15----當(dāng)前類(lèi)名稱(chēng)的索引值為21

      #5 = Class #21 // Test

      常量池6---07 00 16,對(duì)照上面的分析

      #6 = Class #22 // java/lang/Object

      常量池7---01 00 06 3C 69 6E 69 74 3E

      1,01---tag為1,類(lèi)型為CONSTANT_Utf8,根據(jù)官方文檔

      CONSTANT_Utf8_info {

      u1 tag;

      u2 length;

      u1 bytes[length];

      }

      2,06---表示字符串的長(zhǎng)度為6

      3,3C 69 6E 69 74 3E ---字符串

      #7 = Utf8

      常量池8---01 00 03 28 29 56

      常量池9---01 00 04 43 6F 64 65

      常量池10---01 00 0F 4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65

      常量池11---01 00 04 6D 61 69 6E

      常量池12---01 00 16 28 5B 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 29 56

      常量池13---01 00 0A 53 6F 75 72 63 65 46 69 6C 65

      常量池14---01 00 09 54 65 73 74 2E 6A 61 76 61

      #8 = Utf8 ()V

      #9 = Utf8 Code

      #10 = Utf8 LineNumberTable

      #11 = Utf8 main

      #12 = Utf8 ([Ljava/lang/String;)V

      #13 = Utf8 SourceFile

      #14 = Utf8 Test.java

      常量池15---0C 00 07 00 08

      1,0C---tag為11,類(lèi)型為CONSTANT_NameAndType,參照jvm官方文檔,其結(jié)構(gòu)為

      CONSTANT_NameAndType_info {

      u1 tag;

      u2 name_index;

      u2 descriptor_index;

      }

      2,00 07---該字段或方法名稱(chēng)常量索引值為7,即

      #7 = Utf8

      3,00 08---該字段或方法描述符常量索引值為8 ,即

      #8 = Utf8 ()V

      常量池16---07 00 17

      常量池17---0C 00 18 00 19

      常量池18---01 00 04 74 65 73 74

      常量池19---07 00 1A

      常量池20---0C 00 1B 00 1C

      常量池21---01 00 04 54 65 73 74

      常量池22---01 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74

      常量池23---01 00 10 6A 61 76 6A 2F 6C 61 6E 67 2F 53 79 73 74 65 6D

      常量池24---01 00 03 6F 75 74

      常量池25---01 00 15 4C 6A 61 76 61 2F 69 6F 2F 50 72 69 6E 74 53 74 72 65 61 6D 3B

      常量池26---01 00 13 6A 61 76 61 2F 69 6F 2F 50 72 69 6E 74 53 74 72 65 61 6D

      常量池27---01 00 07 70 72 69 6E 74 6C 6E

      常量池28---01 00 15 28 4 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 29 56

      #16 = Class #23 // java/lang/System

      一個(gè)Java程序是怎樣運(yùn)行起來(lái)的【class解析全過(guò)程】

      #17 = NameAndType #24:#25 // out:Ljava/io/PrintStream;

      #18 = Utf8 test

      #19 = Class #26 // java/io/PrintStream

      #20 = NameAndType #27:#28 // println:(Ljava/lang/String;)V

      #21 = Utf8 Test

      #22 = Utf8 java/lang/Object

      #23 = Utf8 java/lang/System

      #24 = Utf8 out

      #25 = Utf8 Ljava/io/PrintStream;

      #26 = Utf8 java/io/PrintStream

      #27 = Utf8 println

      #28 = Utf8 (Ljava/lang/String;)V

      到此常量池結(jié)束

      4、訪問(wèn)標(biāo)志access_flags

      00 21----Test類(lèi)的訪問(wèn)標(biāo)志,參照官方文檔,訪問(wèn)標(biāo)志有

      0x0021 = 0x0020|0x0001,即ACC_PUBLIC和ACC_SUPER為真,ACC_PUBLIC好理解,ACC_SUPER這是什么鬼,翻看官方文檔,原文如下:

      The?ACC_SUPER?flag indicates which of two alternative semantics is to be expressed by the?invokespecial?instruction (§invokespecial) if it appears in this class. Compilers to the instruction set of the Java Virtual Machine should set the?ACC_SUPER?flag.

      The?ACC_SUPER?flag exists for backward compatibility with code compiled by older compilers for the Java programming language. In Oracle’s JDK prior to release 1.0.2, the compiler generated?ClassFile?access_flags?in which the flag now representing?ACC_SUPER?had no assigned meaning, and Oracle's Java Virtual Machine implementation ignored the flag if it was set.

      為了兼容之前的jdk版本,在jdk1.0.2之后這個(gè)編譯出來(lái)的為真

      5,類(lèi)索引,父類(lèi)索引,接口索引

      接下來(lái)就是類(lèi)索引,父類(lèi)索引,接口索引

      00 05------類(lèi)索引值為#5

      #5 = Class #21 // Test

      00 06-----父類(lèi)索引值為#6

      #6 = Class #22 // java/lang/Object

      00 00----類(lèi)沒(méi)有實(shí)現(xiàn)接口,接口數(shù)為0,所以后面沒(méi)有接口信息

      6、字段

      00 00----當(dāng)前類(lèi)有0個(gè)字段

      7、方法,指令

      00 02----當(dāng)前類(lèi)有兩個(gè)方法,參照官方文檔,方法的結(jié)構(gòu)如下:

      method_info {

      u2 access_flags;

      u2 name_index;

      u2 descriptor_index;

      u2 attributes_count;

      attribute_info attributes[attributes_count];

      }

      方法1:00 01 00 07 00 08 00 01

      ----00 01:access_flags=0x0001=ACC_PUBLIC,方法的訪問(wèn)標(biāo)志如下表:

      ---00 07:name_index=#7----->#7 = Utf8 ??????????????,可以看出該方法為構(gòu)造函數(shù)

      ---00 08:descriptor_index=#8------>#8 = Utf8 ??????????????()V

      ---00 01:attributes_count=1,所以緊隨其后就是attribute_info部分,根據(jù)官方文檔,其結(jié)構(gòu)如下:

      Code_attribute {

      u2 attribute_name_index;

      u4 attribute_length;

      u2 max_stack;

      u2 max_locals;

      u4 code_length;

      u1 code[code_length];

      u2 exception_table_length;

      { u2 start_pc;

      u2 end_pc;

      u2 handler_pc;

      u2 catch_type;

      } exception_table[exception_table_length];

      u2 attributes_count;

      attribute_info attributes[attributes_count];

      }

      00 09 00 00 00 1D 00 01 00 01 00 00 00 05 ??//非指令部分

      ---00 09:attribute_name_index=#9---------->#9 = Utf8 ??????????????Code

      ---00 00 00 1D:attribute_length=29,所以整個(gè)屬性表的長(zhǎng)度為29+6=35,見(jiàn)官方文檔說(shuō)明:The value of the?attribute_length?item indicates the length of the attribute, excluding the initial six bytes.

      ---00 01:max_stack=1

      ---00 01:max_locals=1

      ---00 00 00 05:code_length=5

      緊接著就是方法1的指令部分:

      2A B7 00 01 B1

      ---2A:aload_0 ,

      ---B7 00 01 ->invokespecial #1,調(diào)用超類(lèi)構(gòu)造方法

      ---B1--->return

      方法1的Exception:

      00 00:方法沒(méi)有throw異常

      方法1的attribute count:

      00 01://方法1最后有一個(gè)屬性塊,其結(jié)構(gòu)如下:

      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];

      }

      00 0A 00 00 00 06 00 01

      00 00 00 01

      ---00 0A:attribute_name_index=#10---->#10 = Utf8 ??????????????LineNumberTable

      ---00 00 00 06:attribute_lenght=6

      ---00 01:line_number_table_length=1,表示這個(gè)LineNumberTable中有一條記錄

      ---00 00 00 01:表示Test.java的第一行代碼對(duì)應(yīng)指令0--->0: aload_0

      方法2:00 09 00 0B 00 0C 00 01

      ---00 09:access_flags=0x0008|0x0001=ACC_STATIC|ACC_PUBLIC

      ---00 0B:name_index=#11------>#11 = Utf8 ??????????????main

      ---00 0C:descriptor_index=#12----->#12 = Utf8 ??????????????([Ljava/lang/String;)V

      ---00 01:arrtibutes_count=1,緊接著是attribute_info

      方法2的code,非指令部分:

      00 09 00 00 00 25 00 02 00 01 00 00 00 09

      ---00 09:attribute_name_index=#9----->#9 = Utf8 ??????????????Code

      ---00 00 00 25:attribute_length=37,所以整個(gè)屬性表的長(zhǎng)度為43

      ---00 02:max_stack=2

      ---00 01:max_locals=1

      ---00 00 00 09:code_length=17

      方法2的code,指令部分

      B2 00 02----->getstatic #2:獲取指定類(lèi)的靜態(tài)域,并且壓入到棧頂,#2表示#2 = Fieldref ??????????#16.#17 ???????// ?java/lang/System.out:Ljava/io/PrintStream;

      12 03--->ldc #3,將“test”常量值從常量池中壓入到棧頂

      B6 00 04---->invokervirtual ?#4,調(diào)用實(shí)例方法,#4 = Methodref ?????????#19.#20 ???????// ?java/io/PrintStream.println:(Ljava/lang/String;)V,即println方法

      B1---->return

      方法2的Exception:

      00 00 ----->沒(méi)有定義throw

      方法2的attribute_count:

      00 01 //方法最后有個(gè)屬性

      方法2的LineNumberTable:

      00 0A 00 00 00 0A 00 02

      ----00 0A:attribute_name_index=#10------>#10 = Utf8 ??????????????LineNumberTable

      ----00 00 00 0A:attribute_length=10

      ----00 02:line_number_table_lenght=2,表示lineNumberTable中有2條記錄

      00 00 00 04:Test.java第4行對(duì)應(yīng)指令0 --->getstatic ????#2

      00 08 00 05:Test.java第5行對(duì)應(yīng)指令8----->8: return

      8.sourceFile屬性

      00 01:當(dāng)前class文件包含1個(gè)attribute_info

      00 0D 00 00 00 02 00 0E

      ---00 0D:attribute_name_index=#13---->#13 = Utf8 ??????????????SourceFile

      ---00 00 00 02:attribute_length=2

      ---00 0E:sourcefile_index=#14---->#14 = Utf8 ??????????????Test.java

      至此,class文件中的內(nèi)容分析完畢!

      Java

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶(hù)投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:R語(yǔ)言實(shí)戰(zhàn)應(yīng)用精講50篇(三十一)-R語(yǔ)言實(shí)現(xiàn)決策樹(shù)(附R語(yǔ)言代碼)
      下一篇:EfficientPS:一種高效高精度全景分割算法
      相關(guān)文章
      亚洲AV无码1区2区久久| 日本红怡院亚洲红怡院最新 | 亚洲av无码一区二区三区在线播放| 亚洲国产综合自在线另类| 亚洲人成电影亚洲人成9999网| 国产成人A人亚洲精品无码| 亚洲动漫精品无码av天堂| 亚洲成在人线av| 水蜜桃亚洲一二三四在线| 亚洲国产精品自在线一区二区| 久久久婷婷五月亚洲97号色| 亚洲国产一区在线| 亚洲色图在线播放| 亚洲成在人线电影天堂色| 亚洲成a人片在线观看中文!!!| 亚洲国产美女精品久久久久| 亚洲午夜国产精品| 久久亚洲精品国产精品婷婷 | 国产偷v国产偷v亚洲高清| 国精无码欧精品亚洲一区| 亚洲成a人片77777kkkk| 亚洲男人的天堂在线播放| 亚洲第一二三四区| 亚洲综合久久一本伊伊区| 国产精品亚洲片夜色在线| 亚洲人成网亚洲欧洲无码| 国产成人精品日本亚洲语音| 日韩亚洲国产二区| 青青草原亚洲视频| 亚洲国产a∨无码中文777| 亚洲黄色免费在线观看| 亚洲同性男gay网站在线观看| 亚洲永久在线观看| 国产精品亚洲综合一区在线观看| 亚洲爽爽一区二区三区| 国产亚洲一区二区在线观看| 亚洲午夜精品一区二区| 亚洲一区二区免费视频| 亚洲高清乱码午夜电影网| 亚洲人成无码久久电影网站| 日本亚洲欧洲免费天堂午夜看片女人员|