本系列知识80%来自《深入理解Java虚拟机》(周志明)一书,其他部分来自网络再加上一些自己的理解,如有问题请大家指出。
在跟着书学习虚拟机的过程中,确实非常枯燥,但是学过之后在工作中真的有一种原来如此的感觉(好像有点燥)。希望大家坚持学习下去还有我.
开始~
在我们初次学习java的时候,第一行代码一般都是“hello world”,如下:
public class First {
public static void main(String[] args) {
System.out.println("hi");
}
}
在javac后,文件夹下会多处First.class文件,然后java First后命令行就会输出hi。这个过程分为两步:首先通过javac命令将First.java文件编译为java虚拟机可以执行的First.class文件,然后通过java命令执行First.class中的内容。那么虚拟机是怎么通过First.class文件输出hi的呢?下面来一起研究下
我们可以发现.class文件中存储的是一堆16进制数。如果你的英语像我一样Good(.),那么你会发现开头的“字母”“cafebabe”有些奇怪,如果你的知识像我一样渊源(…),那么你会想到与咖啡关系不浅的java在自己的class文件中出现cafe的字样肯定不是偶然,既然如我优秀的你也发现了这些,那么恭喜你,看到了java表演的第一个魔术,哦不,是魔数(Magic Number)。
每一个class文件的头4个字节称为魔数,它的唯一作用是确定这个文件是虚拟机可以接受的class文件。
其实后面¥%……&*的内容跟魔数一样简单,trust me~
先来看看class文件的结构(图片截自网络)
class文件严格按照上图中的部分构成。先来讲解下图中所要表达的意思:类型表示该结构的数据类型,class文件格式只有两种数据类型:无符号数和表。上图中“u”开头数字结尾的为“无符号数”,“info”结尾的为表。
无符号数就是你脑子中的无符号数,没有什么特别的意义,“u”后面的数字表示这个无符号数的长度,单位为字节,即“u1”表示一个字节的无符号数,“u8”表示8个字节的无符号数。
表就是多个无符号数或者表组成的数据结构;
后面是名称,我们大概可以通过名称了解该结构,比如magic就是魔数,minor_version是小版本号。
最后是数量,表示该结构出现的次数。比如魔数数量为1,即出现一次。constant_pool的数量为constant_pool_count - 1,即constant_pool_count的值减去1。
接下来我们用上图中的结构来解读生成的class文件
按照图中结构,class文件首先是一个四位的无符号数,即魔数,对应class文件中的cafe babe。然后是两个字节的小版本号和两个字节的大版本号,分别为0000和0034,换算成十进制为0和50,即小版本为0,大版本为1.8(百度一下);
然后是一个两字节的constant_pool_count意思是常量池中常量的数量(由于常量池中的常量数量是不定的,所有通过这里指定),001d即29,根据上面的结构图可知,常量池中的常量为29 - 1 = 28个。为什么要减一?这是因为001d换算出来的29表示29个位置,即0、1、2、…、27、28,其中0用来表示不引用常量的意思。所以一共有29 - 1 = 28 个常量。
接下来是颇为麻烦的常量池(麻烦表示常量池杂而多~)。
我们先来看下常量池的项目类型(图片来自网络):
这十四种结构中的CONSTANT_MethodHandle_info、CONSTANT_MethodType_info、CONSTANT_InvokeDynamic_info三种是为了更好的支持动态语言在1.7中新增的。
下面根据上图来分析下我们的class文件。001d后面的一个字节是0a即十进制的10,也就是上图中的CONSTANT_Methodref_info:类中方法的符号引用;为了方便分析,这里贴一下常量池中常量项的结构表:
根据上表结构所示,后面应该是两个u2类型的无符号数,分别对应声明字段的类或者接口描述符CONSTANT_Class_info的索引项和字段描述符CONSTANT_NameAndType的索引项,他们所对应的值分别为0006和000f即6和15,也就是第六个常量项和第十五个常量项的值(在之前的分析中已经看到这个class文件中一共有28个常量项)。至于他们具体的值,我们一点一点分析;接下来是第二个常量项,09即CONSTANT_Fieldref_info字段的符号引用,它的结构与Methodref一样,后面是第十六项和第十七项;
第三项#3(下同):
tag:08 CONSTANT_String_info 字符串类型字面量
index:0012 CONSTANT_Utf8_info UTF-8编码的字符串 即#18
#4
tag:0a(10) CONSTANT_Methodref_info
index:0013 CONSTANT_Class_info #19
index:0014 CONSTANT_NameAndType_info #20
#5
tag:07 CONSTANT_Class_info
index:0015 CONSTANT_Utf8_info #21
#6
tag:07 CONSTANT_Class_info
index:0016 CONSTANT_Utf8_info #22
#7
tag:01 CONSTANT_Utf8_info
length:0006
bytes:3c 69 6e 69 74 3e 翻译后为(3c为“<”)
#8
tag:01 CONSTANT_Utf8_info
length:0003
bytes:282956 ()V
#9
tag:01 CONSTANT_Utf8_info
length:0004
bytes:436f6465 Code
#10
tag:01 CONSTANT_Utf8_info
length:000f
4c696e654e756d6265725461626c65 LineNumberTable
#11
tag:01
length:0004
bytes:6d61696e main
#12
tag:01
length:0016
bytes:285b4c6a6176612f6c616e672f537472696e673b2956 ([Ljava/lang/String;)V
#13
tag:01
length:000a
bytes:536f7572636546696c65 SourceFile
#14
tag:01
length:000a
bytes:46697273742e6a617661 First.java
#15
tag:0c CONSTANT_NameAndType_info
index:0007 #7
index:0008 #8
#16
tag:07
index:0017 #23
#17
tag:0c CONSTANT_NameAndType_info
index:0018 #7
index:0019 #8
#18
tag:01
length:0002
bytes:6869 hi
#19
tag:07
index:001a #26
#20
tag:0c
index:001b #27
index:001c #28
#21
tag:01
length:0005
bytes:4669727374 First
#22
tag:01
length:0010
bytes:6a6176612f6c616e672f4f626a656374 java/lang/Object
#23
tag:01
length:0010
bytes:6a6176612f6c616e672f53797374656d java/lang/System
#24
tag:01
length:0003
bytes:6f7574 out
#25
tag:01
length:0015
bytes:4c6a6176612f696f2f5072696e7453747265616d3b Ljava/io/PrintStream;
#26
tag:01
length:0013
bytes:6a6176612f696f2f5072696e7453747265616d java/io/PrintStream
#27
tag:01
length:0007
bytes:7072696e746c6e println
#28
tag:01
length:0015
bytes:284c6a6176612f6c616e672f537472696e673b2956 (Ljava/lang/String;)V
以上就是所有常量池的常量项,我们可以通过命令直接看结果javap -verbose First
结果与我们分析的结果一样。
今天就到这,以后继续分析;