《Java虚拟机原理图解》 1.1、class文件基本组织结构

[last updated: 2014/11/19 09:06]         

      作为Java程序员。我们知道。我们写好的.java 源码,最后会被Java编译器编译成后缀为.class的文件,该类型的文件是由字节组成的文件。又叫字节码文件。那么。class字节码文件中面究竟是有什么呢?它又是如何组织的呢?让我们先来大概了解一下他的组成结构吧。



NO1. 魔数(magic)

      全部的由Java编译器编译而成的class文件的前4个字节都是“0xCAFEBABE”  
      它的作用在于:当JVM在尝试载入某个文件到内存中来的时候,会首先推断此class文件有没有JVM觉得能够接受的“签名”,即JVM会首先读取文件的前4个字节,推断该4个字节是否是“0xCAFEBABE”,假设是,则JVM会觉得能够将此文件当作class文件来载入并使用。


NO2.版本(minor_version,major_version)

      随着Java本身的发展,Java语言特性和JVM虚拟机也会有对应的更新和增强。眼下我们可以用到的JDK版本号如:1.5,1.6,1.7,还有现现在最新的1.8。公布新版本号的目的在于:在原有的版本号上添加新特性和对应的JVM虚拟机的优化。而随着主版本号公布的次版本号,则是改动对应主版本号上出现的bug。我们平时仅仅须要关注主版本号就行了。

主版本号号和次版本号号在class文件里各占两个字节,副版本号号占用第5、6两个字节,而主版本号号则占用第7,8两个字节。JDK1.0的主版本号号为45,以后的每一个新主版本号都会在原先版本号的基础上加1。若如今使用的是JDK1.7编译出来的class文件,则相应的主版本号号应该是51,相应的7,8个字节的十六进制的值应该是 0x33。

      一个 JVM实例仅仅能支持特定范围内的主版本 (MiMj) 和 0 至特定范围内 (0 至 m) 的副版本。如果一个 Class 文件的格式版本为 V。 仅当Mi.0 ≤ v ≤ Mj.m成立时,这个 Class 文件才干够被此 Java 虚拟机支持。不同版本号的 Java 虚拟机实现支持的版本号号也不同,高版本号号的 Java 虚拟机实现能够支持低版本号号的 Class 文件,反之则不成立。

     JVM在载入class文件的时候。会读取出主版本。然后比較这个class文件的主版本和JVM本身的版本。假设JVM本身的版本 < class文件的版本。JVM会觉得载入不了这个class文件,会抛出我们常常见到的"java.lang.UnsupportedClassVersionError: Bad version number in .class file " Error 错误。反之。JVM会觉得能够载入此class文件。继续载入此class文件。

     

    小贴士:

1. 有时候我们在执行程序时会抛出这个Error 错误:"java.lang.UnsupportedClassVersionError: Bad version number in .class file"。

上面已经揭示了出现这个问题的解决办法,就是在于当前尝试载入class文件的JVM虚拟机的版本号 低于class文件的版本号。解决方法:1.又一次使用当前jvm编译源码,然后再执行代码。2.将当前JVM虚拟机更新到class文件的版本号。

2. 如何查看class文件的版本?

 能够借助于文本编辑工具。直接查看该文件的7,8个字节的值。确定class文件是什么版本号的。


当然快捷的方式使用JDK自带的javap工具,如当前有Programmer.class 文件,进入此文件所在的文件夹,然后运行 ”javap -v Programmer“,结果会类似例如以下所看到的:

    



NO3.常量池计数器(constant_pool_count)

 常量池是class文件里很重要的结构,它描写叙述着整个class文件的字面量信息。 常量池是由一组constant_pool结构体数组组成的,而数组的大小则由常量池计数器指定。常量池计数器constant_pool_count 的值 =constant_pool表中的成员数+ 1。constant_pool表的索引值仅仅有在大于 0 且小于constant_pool_count时才会被觉得是有效的。


NO4.常量池数据区(constant_pool[contstant_pool_count-1])

常量池,constant_pool是一种表结构,它包括 Class 文件结构及其子结构中引用的全部字符串常量、 类或接口名、字段名和其他常量。 常量池中的每一项都具备同样的格式特征——第一个字节作为类型标记用于识别该项是哪种类型的常量。称为 “tag byte” 。常量池的索引范围是 1 至constant_pool_count−1。常量池的详细细节我们会稍后讨论。


NO6.訪问标志(access_flags)

       訪问标志。access_flags 是一种掩码标志,用于表示某个类或者接口的訪问权限及基础属性。

      




NO7.类索引(this_class)

       类索引。this_class的值必须是对constant_pool表中项目的一个有效索引值。constant_pool表在这个索引处的项必须为CONSTANT_Class_info 类型常量,表示这个 Class 文件所定义的类或接口。




NO8.父类索引(super_class)

     父类索引,对于类来说,super_class 的值必须为 0 或者是对constant_pool 表中项目的一个有效索引值。

假设它的值不为 0。那 constant_pool 表在这个索引处的项必须为CONSTANT_Class_info 类型常量,表示这个 Class 文件所定义的类的直接父类。当前类的直接父类,以及它全部间接父类的access_flag 中都不能带有ACC_FINAL 标记。对于接口来说,它的Class文件的super_class项的值必须是对constant_pool表中项目的一个有效索引值。constant_pool表在这个索引处的项必须为代表 java.lang.Object CONSTANT_Class_info 类型常量 。假设 Class 文件的 super_class的值为 0,那这个Class文件仅仅可能是定义的是java.lang.Object类,仅仅有它是唯一没有父类的类。



NO9.接口计数器(interfaces_count)

      接口计数器,interfaces_count的值表示当前类或接口的直接父接口数量。




NO10.接口信息数据区(interfaces[interfaces_count])

      接口表。interfaces[]数组中的每一个成员的值必须是一个对constant_pool表中项目的一个有效索引值。 它的长度为 interfaces_count。每一个成员 interfaces[i]  必须为 CONSTANT_Class_info类型常量。当中 0 ≤ i <interfaces_count。在interfaces[]数组中,成员所表示的接口顺序和相应的源码中给定的接口顺序(从左至右)一样。即interfaces[0]相应的是源码中最左边的接口。



NO11.字段计数器(fields_count)

      字段计数器。fields_count的值表示当前 Class 文件 fields[]数组的成员个数。

fields[]数组中每一项都是一个field_info结构的数据项,它用于表示该类或接口声明的类字段或者实例字段。


NO12.字段信息数据区(fields[fields_count])

      字段表,fields[]数组中的每一个成员都必须是一个fields_info结构的数据项,用于表示当前类或接口中某个字段的完整描写叙述。

fields[]数组描写叙述当前类或接口声明的全部字段,但不包含从父类或父接口继承的部分。



NO13.方法计数器(methods_count)

     方法计数器。 methods_count的值表示当前Class 文件 methods[]数组的成员个数。Methods[]数组中每一项都是一个 method_info 结构的数据项。



NO14.方法信息数据区(methods[methods_count])

      方法表,methods[] 数组中的每一个成员都必须是一个 method_info 结构的数据项。用于表示当前类或接口中某个方法的完整描写叙述。假设某个method_info 结构的access_flags 项既没有设置 ACC_NATIVE 标志也没有设置ACC_ABSTRACT 标志。那么它所相应的方法体就应当能够被 Java 虚拟机直接从当前类载入,而不须要引用其他类。 method_info结构能够表示类和接口中定义的全部方法,包含实例方法、类方法、实例初始化方法方法和类或接口初始化方法方法 。

methods[]数组仅仅描写叙述当前类或接口中声明的方法,不包含从父类或父接口继承的方法。




NO15.属性计数器(attributes_count)

     属性计数器,attributes_count的值表示当前 Class 文件attributes表的成员个数。

attributes表中每一项都是一个attribute_info 结构的数据项。


NO16.属性信息数据区(attributes[attributes_count])

     属性表。attributes 表的每一个项的值必须是attribute_info结构。

    在Java 7 规范里。Class文件结构中的attributes表的项包含下列定义的属性: InnerClasses  、 EnclosingMethod 、 Synthetic  、Signature、SourceFile,SourceDebugExtension 、Deprecated、RuntimeVisibleAnnotations 、RuntimeInvisibleAnnotations以及BootstrapMethods属性。

      对于支持 Class 文件格式版本为 49.0 或更高的 Java 虚拟机实现,必须正确识别并读取attributes表中的SignatureRuntimeVisibleAnnotationsRuntimeInvisibleAnnotations属性。对于支持Class文件格式版本为 51.0 或更高的 Java 虚拟机实现,必须正确识别并读取 attributes表中的BootstrapMethods属性。

Java 7 规范 要求任一 Java 虚拟机实现能够自己主动忽略 Class 文件的 attributes表中的若干 (甚至所有) 它不可识别的属性项。

不论什么本规范没有定义的属性不能影响Class文件的语义,仅仅能提供附加的描写叙述信息 。


依据上述的叙述,我们能够将class的文件组织结构概括成以以下这个结构体:






參考书目:

Java虚拟机规范(Java SE 7)中文版(Java_Virtual_Machine_Specification_Java_SE_7)

[深入理解Java虚拟机:JVM高级特性与最佳实践].周志明



作者的话

    本文是《Java虚拟机原理图解》的第一篇。假设您有兴趣,请关注该系列的其它文章~

   认为本文不错,顺手点个赞哦~~您的鼓舞,是我继续分享知识的强大动力。




-----------------------------------------------------------------------------------------------------------------------------------------

                                                                                本文源自  http://blog.csdn.net/luanlouis/,如需转载,请注明出处,谢谢!

posted @ 2017-05-03 11:25  jzdwajue  阅读(177)  评论(0编辑  收藏  举报