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 的标识。
