前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >来自JVM的一封ClassFile介绍信

来自JVM的一封ClassFile介绍信

作者头像
ImportSource
发布2018-04-03 15:24:17
6460
发布2018-04-03 15:24:17
举报
文章被收录于专栏:ImportSourceImportSource

我是一个class文件。我的内部是由一个被叫做ClassFile的structure组成。

我在jvm中占有很重要的地位,你可去看看jvm规范中我占了多少篇幅,告诉你,足足有大半本书!第四章,标题叫做 The class File Format。可以这么说。你把编译器、字节码、jit那些看过以后,再把我搞清楚,基本上jvm你也就精通了。鉴于我这么重要,今天我介绍下自己。我长下面这样:

ClassFile { u4 magic; u2 minor_version; u2 major_version; u2 constant_pool_count; cp_info constant_pool[constant_pool_count-1]; u2 access_flags; u2 this_class; u2 super_class; u2 interfaces_count; u2 interfaces[interfaces_count]; u2 fields_count; field_info fields[fields_count]; u2 methods_count; method_info methods[methods_count]; u2 attributes_count; attribute_info attributes[attributes_count]; }

你也许对这个结构有点疑问,怎么长得这么像c语言的结构体。没错,我们这里就是采用类似c语言结构体的伪结构来表示一个class file的结构。为了说明内部的细节,我们使用了这个伪结构。其实我真实的结构是一组以8位字节为基础单位的二进制流,包含多个数据项,各个数据项严格按照顺序紧凑的排列在Class文件中。其实封面才是我们本来的面目。

为了更友好的说明内部细节,我们还是回到上面那个伪结构,看了这个结构,你也许在纳闷这些u2、u4都是什么鬼。放在一个属性名的前面,有点像类型。没错,基本就是这个意思。在我们classfile的世界上,我们使用一组专用的数据类型来表示class文件的内容。比如u1就代表1个字节的无符号数(unsigned quantity)。u2、u4同理。

在java se platform中,这种类型可以通过java.io.DataInput这个接口中的方法readUnsignedByte,readUnsignedShort, and readInt来读取。

这会你也许又会问那夹杂在u2和u4中间的那些cp_info、field_info以及attribute_info又是什么鬼。其实这个你可以想象下在.java文件中的entity。

什么userinfo啦,orderinfo啦。这里我们把这个结构叫做table。也就是类似数据库里的表结构的样子。嗯,没错。在classfile的世界里,有两种类型,一种就是之前说的那种无符号数,另外一种就是这里的table类型。你再看看这些名字,也就知道大概存储什么东西啦。一个class file结构。肯定得存储这个类有哪些字段吧,那么就存储到field_info这个表里。肯定得存储有哪些方法吧,那么就存储到method_info这个表里。这里边有个cp_info这个是存储什么呢?这个是一个常量池。这个里边存储了方法名、类名、接口名以及源码中那些真正的常量。

其它的项涉及到这些 名称都是存一个常量池的一个索引值。也就是一个引用。

这会你也许又要问。uX和*_info后面那些类似字段名称的东西是什么意思呢。为了避免与类的字段和类的实例等概念产生混淆,这里我们给这些类似字段的东东起了一个新的名词叫:项。英文叫item。没错,再读一遍:item。(ps:好像实在想不起起什么名字,最后都叫做item。就像中文一样,不知道叫什么的时候,就叫“那个东西”、“那个人”)。。。。。起名字真的真的不容易。洪荒之力啊。回归正题!

magic (魔数)

先来来看magic是什么“东西”?在Class文件结构中,最头的4个字节用于存储魔数Magic Number,用于确定一个文件是否能被JVM接受。看起来他唯一的作用也就是jvm用来放行的门禁卡。这个number的值固定就是0xCAFEBABE。。。。。。。。。

minor_version、major_version

好,现在来看看minor_version和major_version。也许你一看就知道了。这两个是class文件的格式版本号。和我们现在说的版本管理基本一个概念。

major就是大版本。minor就是小版本。比如2.1。major_version就是2。minor_version就是1。

constant_pool_count

在版本号之后,就是constant_pool_count。这个是常量池计数器。它的值等于constant_pool中的成员数加1。

constant_pool[constant_pool_count-1]

那么constant_pool前面已经说到了一部分。这里边包含了class文件结构和其子结构中引用的所有字符串常量,以及类名、接口名、字段名和其他常量。

access_flags

access_flags则表示访问标志。下表是class文件的访问标志们:

Flag Name Value Interpretation ACC_PUBLIC 0x0001 Declared public; may be accessed from outside its package. ACC_FINAL 0x0010 Declared final; no subclasses allowed. ACC_SUPER 0x0020 Treat superclass methods specially when invoked by the invokespecial instruction. ACC_INTERFACE 0x0200 Is an interface, not a class. ACC_ABSTRACT 0x0400 Declared abstract; must not be instantiated. ACC_SYNTHETIC 0x1000 Declared synthetic; not present in the source code. ACC_ANNOTATION 0x2000 Declared as an annotation type. ACC_ENUM 0x4000 Declared as an enum type.

  • ACC_INTERFACE

带有ACC_INTERFACE标志的class文件表示是一个接口,而不是一个类,如果没有这个标志的则表示是一个类而不是接口。这里你也许会纳闷,奇怪了,访问标志怎么还会混入一些class的类型内容,这个应该再新增一个项啊。比如叫item_type。不过既然标准就是这么定义的。我们知道就是了。

另外如果一个class文件被设置了ACC_INTERFACE,那么同时也要被设置ACC_ABSTRACT标志。同时就不能再去设置ACC_FINAL、ACC_SUPER或ACC_ENUM了。这个也很好理解,就是互斥。

如果没有设置ACC_INTERFACE标志,那么这个class文件可以具有上面这个表中除了ACC_ANNOTATION外的其它所有的标志。

但要要注意的是,ACC_ABSTRACT和ACC_FINAL是互斥的,你懂的,既然抽象了就必须被用来继承,如果再加个final,就没法被继承了。所以是矛盾的,互斥的。

  • ACC_SUPER

你也许看到了一个奇葩的ACC_SUPER。是不是以你的编程经验怎么都猜不出来这个是干嘛的?没错,有这种感觉就对了。因为这个标志是一种为了弥补jvm设计上的一些过错而增加的。说说这个故事吧。在jdk1.02之前,有个叫invokenonvirtual的指令。在1.02后,这个指令被改名叫做invokespecial。invokenonvirtual的时候没有invokespecial那样只允许调用superclass、private方法或<init>方法。于是在所有的1.02后的class 都必须设置ACC_SUPER这个标志,来表明强加给invokespecial的新的约束必须要被遵守。在1.02 之前ACC_SUPER是没什么鸟用的,甚至就没有ACC_SUPER这个标志。 一般情况下, 调用构造方法或者使用super关键字显示调用父类的方法时, 会使用这条字节码指令。 这正是ACC_SUPER这个名字的由来。 在java 1.2之前, invokespecial对方法的调用都是静态绑定的, 而ACC_SUPER这个标志位在java 1.2的时候加入到class文件中, 它为invokespecial这条指令增加了动态绑定的功能。

  • ACC_SYNTHETIC

好,现在再来说说ACC_SYNTHETIC,此标志意味着该类或接口是由编译器生成的,而不是由源码生成的。

  • ACC_ANNOTATION

另外ACC_ANNOTATION也很简单。annotation类型必须设置这个值。如果设置了ACC_ANNOTATION,那么也必须设置ACC_INTERFACE标志。

  • ACC_ENUM

ACC_ENUM标志标明该类或其父类为枚举类型。

access_flags终于搞清楚了。现在来看看

this_class这个项。这个项的值必须是上面的那个常量池中某一项的有效索引值。常量池在这个索引处的成员必须是CONSTANT_Class_info结构。来表示这个class 文件中所定义的类或接口。

super_class(父类索引)

表示这个class文件所定义的类的直接父类。也是上面的常量池某一项的有效索引值。而且结构也必须是CONSTANT_Class_info结构。

interfaces_count (接口计数器)

这个项的值表示当前类或接口的直接超接口的数量。

interfaces[] (接口表)

就是这个类所实现的接口。里边同样是常量池的索引值。接口表里边的顺序和源代码的接口顺序是一致的。

fields_count (字段计数器)

fields_count项的值是当前类文件fields表中field_info结构体的数量。

fields[] (字段表)

在fields中的每个值都必须是field_info结构。表中的每个值都完整的描述了该类或接口中的一个字段。fields表中只包含当前类或接口中的字段,不包含超类或super 接口中的字段。

methods_count (方法计数器) methods_count项的值是当前类文件方法表中method_info结构体的数量。

也就是类文件中方法的数量。

methods[] (方法表)

methods表中的每个成员都必须是一个method_info结构,用来表示当前类或接口中的某个方法的完整描述。

attributes_count (属性计数器) attributes_count的值是该类中的attributes表中属性的数量

attributes[] (属性表)

同样的上面的各种表一样。里边的每个值都必须是attribute_info结构。这个属性也许你理解起来有点抽象,不知道是什么。这时候你可以想想一个类文件结构那么复杂。比如一个方法中那么多代码是怎么表示的呢?其实这些事情就是由jvm中预定义好的属性来表示的。 jvm中有很多的预定义属性。其实也是一个结构。类似今天class file的结构。你可以这样认为,一个类类或接口这么复杂。就像我们做一个应用一样,需要设计很多数据表来实现数据存储。你可以把这些预定义属性理解为jvm中的数据表。有关属性的内容我们以后会专门抽出一期来讲解这方面的内容。

好了,鉴于公号文章篇幅限制,况且你也一下子看不完几十页,本文就介绍这么多。希望通过这封介绍信能够让你了解我!了解JVM!作为吃瓜群众,今天还有更重要的事情要做!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2016-08-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 ImportSource 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据保险箱
数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档