jvm class 文件格式 官网
)Class 文件(Class File Format)
的十六进制的布局、含义,看class文件的几个工具。即任何语言只要编译成 class 文件,在装有JVM任何的系统上 都可以运行。
即:有好多语言是在JVM上运行的,这就是夸语言的。
内存
中进行装载,装载完成后, 然后调用 字节码解释器
或者JIT即时编译器
来进行解释 或者 编译。解释和编译是可以混合的
,如果代码用到的次数比较多,会把代码做成 即时编译 即做成本地的编译,就类似c语言在win中编译成exe文件,这样效率会比较高。JVM:是跨语言的平台。
java:是跨平台的语言。
在JVM上能够跑的语言 到目前有100多种,比如下图中的Scala、groovy语言等。
== jvm跟java无关==
https://docs.oracle.com/en/java/javase/13/
https://docs.oracle.com/javase/specs/index.html
白话解释:
JVM是一台虚拟出来的一台机器,也就有自己的CPU、内存管理,比如栈、堆、方法区,所以也就有后面的JVM调优等等。
Hotspot
Jrockit
J9 – IBM
Microsoft VM
TaobaoVM
LiquidVM
azul zing
Hotspot:就是上面所说的第一个JVM类型
mixed mode:就是 上面 1.4说的混合模式,解释和编译混合执行。
jdk全称:java development kit
,其意思是java开发工具包
。jdk是sun公司开发的,jdk包括jre(java runtime environment)java运行环境,一堆java工具[java的编译器(java c.exe),java解释执行器(java.exe)]和java基础的类库(有3000多类,常用的类150多个)。
JRE(Java Runtimely Environment)
,java运行环境,只能运行.class文件,不能编译,针对用户。JRE,包含一个JVM(java虚拟机),与java核心类库与其所支持的文件。与JDK不同,它不包含开发工具—编译器,调试器和其他工具。
JVM(java Virtual Machine )
,Java虚拟机,Java运行环境。Java虚拟机,是一种虚拟出来的计算机,是通过在实际的计算机上模拟仿真各种计算机功能来实现的。
分析和学习class文件。目前公司面试很少用到,但是需要学习和了解哈,抱着兴趣去学。
不能抱着功利性去学。
class文件:就是编译完成之后的 .class
文件
最简单的测试小程序,就是一个类,是为了方便观察其编译后的内容,然后由简到繁,逐步学习。
package com.mashibing.jvm.c1_bytecode;
public class T0100_ByteCode01 {
}
然后编译,生成 T0100_ByteCode01.class
文件。
如果在idea中打开编译后的 T0100_ByteCode01.class
的文件,就是idea会帮我们进行反编译
,反编译的多了一个默认的无参构造函数,这是默认添加的。
注释是反编译出来的注释
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.mashibing.jvm.c1_bytecode;
public class T0100_ByteCode01 {
public T0100_ByteCode01() {
}
}
任何的文件都应该是010101二进制,class文件
如果用一个16进制编辑器(sublime text 3
)打开,就是如下图所示:
class文件
是 二进制字节流。
数据类型:u1 u2 u4 u8 和 _info (表类型)(只是逻辑上分的,其实没有数据类型,只有0和1
)(u:Unsigned,意为为无符号的,u1指1个字节;u2指2个字节;u3指3个字节;u4指4个字节;u8指8个字节
)
_info
的来源 是Hotspot 源码中的写法查看16进制格式classFile
的工具
sublime
(打开如上图) / notepad
idea插件:Bined
(上面的红框可以查看二进制、八进制、十六进制的文件,当然二进制是最根本的文件格式)
有很多可以观察byteCode的方法
:
javap
(java自带):下面有使用JBE
可以直接修改JclassLib
idea插件之一 (下面以这个工具主要讲解)idea自带的
:JclassLib
后classfile构成
看博文开头的xmind文件。
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];
}
class就是二进制字节流,那怎么解释呢,看由谁来解释,这里是由java虚拟机 JVM 来解释的
,为了方便下面以16进制进行查看二进制字节流,十六进制也是 jvm 的规范进行解读。
每个十六进制 对应 jvm 制定规范的 含义和指令。比如下面的十六进制 CAFE BABE 对应就是magic number ,含义就是class文件的开头,
提前剧透:常量池是class文件里最复杂的部分
下图就是上面2.2 第一个图片,编译后的class文件,对其进行解读。
== 讲解上图:==
一个小格是一个字节。对十六进制来讲,一个十六进制就是4位。两个十六进制就是一个字节 即8位。(如下图可看出,第一个小格是CA,这是16进制,所以这是一个字节,即8位。)(1字节可以包含两个16进制数
)
CAFE BABE
这是java编译后class文件
的抬头,比如其他png、GIF的都有自己独有的抬头。这就是 magic number
魔法数。
0000 0034
中,
0000
为 minor version
,小版本号,比如版本是52.0,则minor version就是.0
的概念0034
是 major version
,大版本号,十进制是52,1.8编译完后就是52,1.9就是53。1110
:constant_pool_count
,常量池里存在常量的个数
;两个字节,216=65536,,最多可放65535个常量
最多是 - 1。
接着是常量的表constant_pool
:这里就是存常量池的地方,这里面放的是类的一些信息,比如类名、方法、参数等等。如xmid文档,截图部分如下图。
其长度为constant_pool_count - 1
的表;这里是10,十六进制 就是15,则 就是15-1=14个常量,为什么要减一,因为常量池数组是从1开始的(平时数组是从0开始)因为最前面保留了一个0,将来可能有一些引用指向会表示不指向任意常量的任何一项,就可以用0来代表。
access flags
:访问标识,比如public、private、protect、final等。
后面的依次进行 翻译,这里就略过,每个十六进制数
都对应jvm的设置好的含义或者是指令
。可以主要看博文最开始的 xmind文件以及jvm class 格式 官网。
这样看不是很清楚,可以通过工具来很清晰的查看,就是java自带的javap
:会把class文件中的内容帮我们翻译好。(从2.2中可以看到)
javap T0100_ByteCode01.class
:显示内容较少,如下,(可通过javap查看参数)
javap -v T0100_ByteCode01.class
:下图中可以看到帮我们翻译出来的 minor version、major version、flags 等名称。
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
:后面的 ACC_SUPER 就是:该标志必须为真,JDK1.0.2之后编译出来的内容必须为真,指明invokespectial指令使用新语义
使用 jclasslib 打开 class 文件,下面并进行分析和说明
这里包含了class 文件结构的大多数基础信息,当然最重要的还是常量池。
cp_info #7 :
后面的
就是本类的名称,cp_info #7
就是在常量池的7号存的。绿色可以点击,点击过去就是 常量池的7号位置。cp_info #2
:后面的还是父类名称,前面就是父类存储在常量池的2号位置。下图的思维导图可从文章开头的前言中找到并下载
)jclasslib 打开的常量池 1号位置
是 Methodref_info
文件:存的是方法引用信息,从思维导图中可以看出存的如下图所示,包括三个 1是标记10,2是index2个字节指向其他常量池 ,3是index2个字节指向其他常量池。1号位置 methodref_ref
中的 类名 cp_info #2
:意思是指向类的名字在2号位置,这里又存在了4号位置。1号位置 methodref_ref
中的 描述 cp_info #3
:指向的是3号位置的name和type。可以看出
: 是构造函数<()v>
: ()
是指 没有参数,V
是指返回类型为void类型。因为2.1测试小程序很简单,没有接口和字段,所以这里为空
在一般信息中,也可以看出个数,如下
测试小程序虽然很简单,啥都没有
但是生成一个默认的 构造函数,调用的父类也就是java.lang.Obeject的构造方法
。
这里也有最重要的code 环节,因为函数里会有code的哈,这里会有大量的指令集(JVM大约定义256个左右)
就是默认的构造函数,从指令的右边可以看出 调用的是父类的默认的构造方法。
code
就是重要的代码部分,右边的上面也有指向常量池的类名称等信息,从这里也可以看出,类方法信息也都存在了常量池中。
JVM 13 版本 的第七章,是十六进制码 与 指令的 对照。即 十六进制码 代表了什么意思,方法的具体实现翻译成class文件都变成了第七章(下图)的 十六进制(本质二进制)和指令(十六进制的映射),也就是 java的汇编语言
。
当前在字节码中,有三个 指令。
指令的翻译可以从官网中查找对应的意思。
第一个是 aload_0
:通过上面文档可以找到
(也可以通过鼠标在 jclasslib 中点进去如下图),
aload_0
对应的是 0X2a
这个十六进制,可以在class文件的十六进制文本中找到 2a
这个位置,这个位置对应的就是 aload_0
指令,那么 这个指令的作用是干嘛的呢。
在文档中检索找到此位置,这个汇编指令代表的是:把本地变量表中的第0项(只要不是静态方法就是 this)放到栈里,然后执行第二条指令 invokespecial
。
invokespecial
:可以看到右面的 #1
可以点进去进行跟踪。在文档找到描述,如下图,可以看到对应的16进制是 0Xb7
。加载到栈中,然后是第三条指令,也就是最后一条指令。format中下面两个indexbyte1和indexbyte2 是指令的两个参数 。
return
返回指令,对应 b1
。
构造函数
的 三个指令 体现在十六进制的 class文件中如下所示。
这里默认的就是java的类名称
上面主要讲解了class文件的十六进制由JVM解释后的内容,通过JVM文档进行对应解释。
除了常量池之外的,存的内容都是常量池中的地址。
由常量池存放除常量池之外的其他内容的引用地址。
package com.mashibing.jvm.c1_bytecode;
import java.io.Serializable;
public class T0101_ByteCode_With_Interfaces implements Cloneable, Serializable {
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.mashibing.jvm.c1_bytecode;
import java.io.Serializable;
public class T0101_ByteCode_With_Interfaces implements Cloneable, Serializable {
public T0101_ByteCode_With_Interfaces() {
}
}
多了两个实现的接口,可以看到存在了常量池。
package com.mashibing.jvm.c1_bytecode;
public class T0102_ByteCode02 {
void m() {
int i=0;
int j = i++;
}
}
package com.mashibing.jvm.c1_bytecode;
public class T0102_ByteCode02 {
public T0102_ByteCode02() {
}
void m() {
int i = 0;
++i;
}
}
可以看到 自定义方法和构造函数也都放到了常量池中。
package com.mashibing.jvm.c1_bytecode;
public class T0103_ByteCode03 {
int i = 0;
String s = "Hello ByteCode!";
}
package com.mashibing.jvm.c1_bytecode;
public class T0103_ByteCode03 {
int i = 0;
String s = "Hello ByteCode!";
public T0103_ByteCode03() {
}
}
类属性,即类成员变量,也存放在了常量池中。
package com.mashibing.jvm.c1_bytecode;
public class T0104_ByteCode04 {
int i = 0;
String s = "Hello ByteCode!";
public T0104_ByteCode04(int i, String s) {
this.i = i;
this.s = s;
}
}
package com.mashibing.jvm.c1_bytecode;
public class T0104_ByteCode04 {
int i = 0;
String s = "Hello ByteCode!";
public T0104_ByteCode04(int i, String s) {
this.i = i;
this.s = s;
}
}
红框中是函数参数类型和返回值类型。
package com.mashibing.jvm.c1_bytecode;
public class T0104_ByteCode05 {
int i = 0;
String s = "Hello ByteCode!";
public T0104_ByteCode05(int i, String s) {
this.i = i;
this.s = s;
}
public void m() {}
}
package com.mashibing.jvm.c1_bytecode;
public class T0104_ByteCode05 {
int i = 0;
String s = "Hello ByteCode!";
public T0104_ByteCode05(int i, String s) {
this.i = i;
this.s = s;
}
public void m() {
}
}
这是java 19版本的 JVM
https://docs.oracle.com/javase/specs/jvms/se19/html/index.html
download
document download
中 下面的 read me
,点进去jdk document
中的红框中的第一个,(这两个都有用)。specifications
中的 language and VM
这就是我们此行的目的地了从上面的最后一张截图所在的网址里,就可以学习 class 文件 格式的内容,也就是博文最开始总结的 xmind 的内容。
下图中就是class 文件 中 最重要的 常量池中的 tag 的标识。