理解JVM的class文件结构

来源:互联网 发布:python 字符串 查找 编辑:程序博客网 时间:2024/05/18 01:17

理解JVM的class文件结构

  

     开发者将Java代码写在.java文件中,经过编译器的编译生成了class文件,原来在Java文件中定义的信息将转存到class文件中。只有经过编译后的class文件才能被虚拟机理解,JVM才能将文件中的内容转换成其自己的内存结构。这个过程中,开发者和jvm好比不能沟通的两个人,但是开发者可以通过翻译(编译器)将他自己才懂得Java翻译成class,这样jvm就能识别。

 

   class文件的生成和解析过程跟序列化和反序列化也极为类似,只不过序列化跟反序列化的过程是对称的,class文件的生成和解析过程并不对称。但是对于从一个二进制流中读取出自己想要的信息这件事情来说,他们是一致的。

    

一,序列化与反序列化

     若想实现序列化和反序列化,首先发送端(序列化的一端)和接收端(反序列化的一端)要协商好消息的格式。这是一条必须满足的条件,而且可以只满足这个条件。例如发送端现在需要传输一批int类型的id到接收端,每个id需要32位四个字节。这里每四个字节表示一个id就是发送端和接收端协商好的格式,接收端只需将所有的二进制数据按四个字节四个字节的换成int型的id即可。

 

    如果问题稍微复杂一些,现在需要传输一批用户的名字到接收端,而且名字的长度不是固定的。再像刚才那样直接从二进制数据中读取一定得字节的方案已经不可行了,因为接收端要想读取一个人的名字根本就不知道要读几个字节。这时候可以在发送一个名字之前,先发送若干个固定的字节(假设两个字节)来表示这个名字的长度。这样发送出去的序列就是  :

                    长度、名字、长度、名字、长度、名字..........

   接收端就可以不断地先读取长度,然后按照长度值读取出对应的名字,再然后继续读取长度,读取名字...。

 

   若问题再复杂一些,现在需要同时传输每个用户的id和名字到接收端,而且有可能先发送id也可能先发送名字,但是不同用户的名字和id是不会交叉发送的(若交叉发送是根本解不出来的,这样的协议有问题)。那么,接受端改怎么从二进制数据中读取用户的id和名字呢?如果知道接下来的几个字节表示的是id,则可以直接读入表示id的四个字节;如果知道接下来的几个字节表示的是名字,则可以先读入表示名字长度的两个字节,在根据读入的长度继续把名字读出来。但是,如何才能知道接下来的字节是什么类型的数据呢?是否可以先发送一个字节来说明接下来的数据是何类型呢?问题当然可以这么解决,比如0x01用来表示id,0x02表示名字,那么传送的序列可能就是:

             0x02长度名字0x01id0x01id0x02长度名字...........

 

     现在,不论问题再如何复杂,都可以通过上面的方法得以解决。

 

二,class文件的结构

    若想让JVM读懂class文件,它在结构也要满足上面的要求。即

1,如果知道下面的数据的类型,则可以直接读取

2,若不知道数据的类型,则需要若干字节表示数据类型

3,若数据的长度是不固定的,则需要若干字节来表示此数据的长度

 

   上面所说的数据的最小单位均是字节,其实并不是必须这样,也可以是更小或者更大的单位。只是class文件以8位字节数据构成。下面来看看它的具体构成,可以利用javap命令打印出class文件的内容:

 

a,魔数

      所有class文件的前四个字节的内容都是0xCAFEBABE,它被称作魔数,用来辨别此文件是否为虚拟机可以识别的class文件,Java是一个跟咖啡渊源很深的语言。类似魔数的技术,在gif或JPEG文件中都有应用。

b,版本号

     紧接着魔数后面的四个字节表示的是class文件的版本号,高版本的JDK可以向下兼容低版本的class文件,但是低版本的JDK不能运行高版本的class文件,即使它们的结构是一致的。

c,常量池

     常量池存放字面量和符号引用等信息,这些数据的数量是不固定的,所以需要两个字节来表示常量池中元素的数量。常量池中的常量的长度也是不固定的,所以每个常量开头都有一个字节来表示常量的类型。

d,访问标志

   常量池后的两个字节为类的访问标志,用来描述当前类是否为接口,是否为public,是否为final等。

e,类索引,父类索引、接口索引

    Java中的类拥有一个父类(Object除外),可以拥有若干个接口。所以,class文件中用两个字节表示类信息在常量池中的索引,另外两个字节指向常量池中描述父类的常量。接下来用一组两个字节(先有两个字节表示接口的个数)的索引指向常量池中的接口信息。

f,字段表集合与方法表集合

   接下来就是class中的主要信息,当前类中声明的字段、方法。具体其中会用很多复杂的类型。

    

 

 

0 0