本文首发于微信公众号【程序员江湖】

作者黄小斜,斜杠青年,某985硕士,阿里 Java 研发工程师,于2018 年秋招拿到 BAT 头条、网易、滴滴等 8 个大厂 offer

个人擅长领域 :自学编程、技术校园招聘、软件工程考研(关注公众号后回复”资料“即可领取 3T 免费技术学习资源)

Java的代码可以一次编译,到处运行,是因为JVM可以识别class文件,JVM虚拟机和物理机的效果是一样的,有一套指令集让代码翻译成对应的操作,只不过JVM指令集最终还要去执行物理机的指令集,比如X86。

JVM指令集简介

oolong编程语言是一种汇编语言,我们可以将class文件先转化为oolong汇编语言,以便理解。实际上JVM直接基于字节码运行,不需要经过中间的汇编过程,但是我们依然可以先把它翻译成汇编,这些汇编代码可以很好地对应JVM提供的指令集。

如果直接用class文件去匹配jvm指令集,肯定是不太现实的。

与类相关的指令

这部分指令主要与类信息相关,譬如原文件名,类名,父类,以及修饰符等等,一般类中的操作都有对应的JVM指令相对应(这里的指令指的是汇编指令)

方法的定义

方法的定义包括修饰符,方法名,操作函数等等,也有其对应的指令。比如invokervirtual调用实例方法。

属性的定义

属性定义包括了数据类型,方法的修饰属性,类的修饰属性,等等。

其他指令集

由于JVM的指令集是基于栈进行操作的,所以也有与栈操作相关的指令集,还有与运算相关,与数组操作相关的指令集等等。

这里也有我们熟悉的同步操作相关指令集,monitor enter和monitor out,这个汇编指令可以帮助jvm完成同步操作。

class文件头的表示形式

class文件的内容是顺序排列的。

第一行是一个标识符,是"cafebabe",表明这个文件是一个class文件。

后面两个字节表示版本分为。

所以前6个字节是表示class文件的基本头信息,jvm加载class时会检查其是否符合条件。

常量池

接下来到了常量池部分。

第一行有两个字节表示该类中含有常量的总数,有十几种类型

这些常量通常都是相互引用的。

基本数据类型

Integer,Float,Long等等

UTF-8常量类型

一般用于存储字符串值

fieldref、methodref

这两个类型很明显是为了描述class中的属性项和方法的,如何表示一个class中的属性和方法呢,比如fieldref,前两个字节表示是哪个类中的field,后面两个字节表示这个fieldref的name和type。

methodref和fieldref也类似。

所以methodref和fieldref存的是类名称和nameandtype

class常量类型

class常量表示的是该类的名称,会指向另一个UTF-8类型的常量来存储具体名称,因为名称是字符串啦。

所以class常量中存的是索引。

nameandtype

nameandtype是为了表达methodref和fieldref的名称和类型描述才存在的,名称通常用utf8来表示,类型描述也用utf8来表示。

所以nameandtype主要包含两个utf8的位置索引

类信息

常量列表的后面就是类本身的信息描述了。比如这个类的访问控制。名称和类型,以及父类信息等。

fields和methods定义

类信息描述后面就是每个fields和methods的具体定义了,刚才的methodref和fieldref其实就是索引到了这一部分的内容。

类属性描述

和field和method一样,class同样也有附加属性描述。

javap生成的class文件结构

除了通过oolong生成class文件格式,也可以通过javap来生产class文件格式。这个文件格式更容易理解。