从JVM设计者的角度来看.class文件结构,一文弄懂.class文件的身份地位

671 阅读6分钟

目录

本文相关虚拟机的命令

  1. 编写Java源码
Java源码
Java源码
  1. 编译指令 javac xxx.java,得到.class文件
  2. hexdump -C filename可以查看二进制文件
.class 二进制文件
.class 二进制文件
  1. 编译后使用 javap -c 类名, 得到.class文件对应的虚拟机指令 class文件对应的虚拟机指令
class文件对应的虚拟机指令表
class文件对应的虚拟机指令表

这里先让大家大致的看看.class文件和虚拟机指令的样子,不会让你有种“所爱隔山海”的感觉。

Class文件产生背景及重要地位

【问题】只有Java语言编译后的class文件才能在 JVM 里面跑吗?

首先抛一个问题,是不是只有Java语言编译后的class文件才能在jvm中运行,也就是说其他的语言是否可以使用Java虚拟机作为媒介? 答案是:不是,可以的;只要符合JVM文件结构的规范,编译后的文件均能运行,比如scala语言.scala结尾文件,可以编译为.class文件,可以在jvm中运行,哈哈哈,这里写的好生硬。

各种语言与JVM的爱恨情仇
各种语言与JVM的爱恨情仇

如图各种语言与JVM的爱恨情仇,只要你的语言经过自己的编译器,最终能得到一个JVM需要的.class文件,那么你就可以。

class文件的地位

  • Source: 源码
  • Class: 字节码
  • Runtime: 运行时

字节码形态经由Classloader加载变成运行时形态(内存中)。

java 刚刚诞生的口号“一次编写,到处运行",这是满足开发人员对冲破平台限制渴望的实现,这个很快就实现。然而设计者一开始发布规范文档的时候就是刻意把Java规范拆解成了《Java语言规范》和《Java虚拟机规范》,也就是说他们对虚拟机的野心不止于Java语言,他们未来的目标是冲破语言限制,现在也实现了,Scala,jRuby这些语言都可以运行在Java虚拟机上面。随着平台(windows,linux,unix)基本稳定,而语言日新月异,日后JAVA“语言无关”的优势可能会超过“平台无关”的优势!

实现语言无关的关键就是虚拟机和字节码存储格式。可以理解成虚拟机给你提供一个接口,你只需要按照他指定的格式传class文件给他,他便可以运行你的代码,所以关键就是这个class文件。虚拟机不关心你的项目使用什么语言进行代码实现,最后你只需要按照他想要的class格式编译成class文件传给他,就可以成功运行在虚拟机上面。我说的够清楚了吧老弟?

Class 类文件结构设计解析

class 文件是一组以8位字节为基础单位的二进制流,中间没有任何分隔符,正是因为没有任何分隔符所以class文件里面的数据项在顺序和数量上面是严格限定的,每个字节的含义,长度,先后顺序,都不允许改变。具体class文件内容看图。

class文件采用的是一种类似于C语言结构体的伪结构来存储数据,本质就是一张表,如图所示:

.class 二进制文件
.class 二进制文件
class文件对应内容
class文件对应内容

【问题】你会怎么设计class文件?

首先抛出一个问题,JVM一开始就是服务Java的,拿Java语言来举例子,如果你是设计者,你将你的Java源码编译成class文件,你会怎么设计这个class文件?

思路:转换成容易理解的实现,知道Java的,一般都知道xml文件,一个JavaBean完全可以转化为用xml这种描述性语言来表示,原因是xml是指定好了协议,你的Java要变成 xml 就要按照我的协议来办事,用 xml 描述一个学生对象如下:

xml 表示的Java对象
xml 表示的Java对象

协议的话其实就很能理解,你的class是一个十六进制文件,那么就必须制定自己的协议啊,使得你的十六进制描述的东西可以转化为 JVM 可以理解的虚拟机指令,也就是你的 “xx” 这几个字母是为了告诉虚拟机干啥,“oo”这几个字母是想告诉虚拟机干啥,“xxoo”又是表示啥,这里先看看实际的JVM指令以及和class文件对应关系(现在的 JVM 指令有255个大约)

JVM指令表
JVM指令表

转化:有了思路就好办了,转化一下,将 JAVA 源码转化为 CLASS 文件,来看看具体怎么转化:

  1. 一个Java类对应一个class文件(有可能是多个,如果类里面有内部类),里面有哪些东西?肯定不能丢东西嘛,这是最基本的要求,如果人家明明写的是“你绿了我吗”,你给搞成了“你绿了我”,那这心情起伏还是蛮大的哈。
  2. Java类里面的东西你怎么剖析,怎么设计存储?
  • 简单解剖一下,class文件可能是Java中的class类,也可能是接口,一个class所表示的里面还可能不止一个类和接口,得区分一下吧? 来,敲黑板划重点,大胆设计就是接口类型集合,普通类集合,还不止一个?再加两个字段接口个数,类个数,一切都是这么的完美,往class文件里面找,我去都能找到,OK,我是天才,下一个。
  • Java里面有属性,有方法,有常量,有字段,怎么破? 继续嘛,集合搞起来,个数存起来,最终得到了上面图里面的表结构的class文件。

再来回顾一下class文件内容: class文件对应内容

  1. Java类里面有什么东西,class文件应该存储些什么东西?
  • 两种数据类型:无符号数和表 无符号数属于基本数据类型(Java类中也有基本数据类型),以 u1,u2,u4,u8这种来代表1个字节,2个字节,4个字节,8个字节的无符号数,可以用来描述数字,索引引用,数量值或者字符串值。 就跟Java类中的对象引用类型一样,对象属性可以是基本数据类型(对应U1,U2无符号数),也可以是其他的对象(对应其他的表),Java工程项目中参数实体通常以"_Param"结尾(class文件的表都习惯以“_info”结尾)
  • 上图中的顺序,就是Class文件严格要求的顺序
  • 各个计数器主要是用来描述表里面数据个数,例如方法计数器的值是methods_count,代表方法表method——info里面有“methods_count”个方法
  • 整理一下,表结构用C语言表示类似下面的伪代码(忽略idea报错的红线)
class文件代码表示
class文件代码表示

完结撒花了?这里我是从宏观上讲解了下.class文件,下期进行.class类文件结构详细解析,以及字节码指令的剖析,本篇只是个开胃菜,下一篇内容才是主菜。 另外Java类在JVM里面的生命周期可以看我公众号上一篇内容