JVM知识体系学习一:JVM了解基础、java编译后class文件的类结构详解,class分析工具 javap 和 jclasslib 的使用

前言

  1. 文中所用到的class文件结构思维导图下载:class文件思维导图(这个思维导图的来源是下面 的 jvm class 文件格式 官网
  2. jvm 13版本 规范 HTML 版本:https://docs.oracle.com/javase/specs/jvms/se19/html/index.html
  3. java 各版本和 JVM各版本下载:https://docs.oracle.com/javase/specs/index.html
  4. 本博文是以jdk8版本学习的,文档是13版本。
  5. 本博客是讲:
    • JVM的一些基础、跨平台的语言和跨语言的平台。
    • 字节码 Class 文件(Class File Format) 的十六进制的布局、含义,看class文件的几个工具。
  6. 下个博文讲,class文件如何加载(load)到内存以及加载的。
  7. 知识体系分布:
    1. JVM知识体系学习一:JVM了解基础、java编译后class文件的类结构详解,class分析工具 javap 和 jclasslib 的使用
    2. JVM知识体系学习二:ClassLoader 类加载器、类加载器层次、类过载过程之双亲委派机制、类加载范围、自定义类加载器、编译器、懒加载模式、打破双亲委派机制
    3. JVM知识体系学习三:class文件初始化过程、硬件层数据一致性(硬件层)、缓存行、指令乱序执行问题、如何保证不乱序(volatile等)
    4. JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配

一、JVM基础

1、cross platform 跨平台

即任何语言只要编译成 class 文件,在装有JVM任何的系统上 都可以运行。

在这里插入图片描述

2、cross language 跨语言

即:有好多语言是在JVM上运行的,这就是夸语言的。
在这里插入图片描述

3、什么是JVM呢?一张图告诉你

在这里插入图片描述

4、java从编码到执行*****

  1. 通过 javac 命令:将 java文件 编译成 class 文件
  2. 通过 java 命令:classloader 加载 class 文件以及 java 类库 到内存中进行装载,装载完成后, 然后调用 字节码解释器 或者JIT即时编译器 来进行解释 或者 编译。
  3. 之后 由 执行引擎 进行 执行,最终到OS操作系统。
  • 那java是解释执行的还是编译执行的呢?其实 解释和编译是可以混合的,如果代码用到的次数比较多,会把代码做成 即时编译 即做成本地的编译,就类似c语言在win中编译成exe文件,这样效率会比较高。
    在这里插入图片描述

5. 从跨平台的语言到跨语言的平台

JVM:是跨语言的平台。
java:是跨平台的语言。
在JVM上能够跑的语言 到目前有100多种,比如下图中的Scala、groovy语言等。
在这里插入图片描述

6. jvm与class文件格式

== jvm跟java无关==
在这里插入图片描述

7. JVM

  • jvm是一种规范 – java virtual machine specifications
    • https://docs.oracle.com/en/java/javase/13/
    • https://docs.oracle.com/javase/specs/index.html
  • JVM 是 虚构出来的一台计算机
    • 字节码指令集(汇编语言)
    • 内存管理:栈 堆 方法区等

白话解释:
JVM是一台虚拟出来的一台机器,也就有自己的CPU、内存管理,比如栈、堆、方法区,所以也就有后面的JVM调优等等。

8. javac的过程

在这里插入图片描述

9. 常见的JVM实现

  1. Hotspot

    • oracle官方,我们做实验用的 JVM
    • java –version
  2. Jrockit

    • BEA,曾经号称世界上最快的 JVM
    • 被Oracle收购,合并于hotspot
  3. J9 – IBM

  4. Microsoft VM

  5. TaobaoVM

    • hotspot深度定制版 ▪
  6. LiquidVM

    • 直接针对硬件 ▪
  7. azul zing

    • 最新垃圾回收的业界标杆
    • www.azul.com

在这里插入图片描述
Hotspot:就是上面所说的第一个JVM类型
mixed mode:就是 上面 1.4说的混合模式,解释和编译混合执行。

10. JDK JRE JVM

  • 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 File Format (class 文件格式)

分析和学习class文件。目前公司面试很少用到,但是需要学习和了解哈,抱着兴趣去学。

不能抱着功利性去学。
class文件:就是编译完成之后的 .class 文件

1、测试小程序

a、T0100_ByteCode01.java

最简单的测试小程序,就是一个类,是为了方便观察其编译后的内容,然后由简到繁,逐步学习。

package com.mashibing.jvm.c1_bytecode;

public class T0100_ByteCode01 {
}

然后编译,生成 T0100_ByteCode01.class 文件。

b、idea打开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() {
    }
}

2、class文件

  • 任何的文件都应该是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的方法

    1. javap(java自带):下面有使用
    2. JBE 可以直接修改
    3. JclassLib idea插件之一 (下面以这个工具主要讲解)
      在这里插入图片描述
      未下载前 是idea自带的
      将鼠标放在class文件或者Java文件中:
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      不好观察,所以下载插件: JclassLib
      将鼠标放在class文件或者Java文件中:
      在这里插入图片描述
      在这里插入图片描述
  • 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];
    }
    

3、Class文件解读

  1. class就是二进制字节流,那怎么解释呢,看由谁来解释,这里是由java虚拟机 JVM 来解释的,为了方便下面以16进制进行查看二进制字节流,十六进制也是 jvm 的规范进行解读。

  2. 每个十六进制 对应 jvm 制定规范的 含义和指令。比如下面的十六进制 CAFE BABE 对应就是magic number ,含义就是class文件的开头,

  3. 提前剧透:常量池是class文件里最复杂的部分

下图就是上面2.2 第一个图片,编译后的class文件,对其进行解读。
在这里插入图片描述
== 讲解上图:==

一个小格是一个字节。对十六进制来讲,一个十六进制就是4位。两个十六进制就是一个字节 即8位。如下图可看出,第一个小格是CA,这是16进制,所以这是一个字节,即8位。)(1字节可以包含两个16进制数

  • CAFE BABE 这是java编译后class文件的抬头,比如其他png、GIF的都有自己独有的抬头。这就是 magic number 魔法数。

  • 0000 0034 中,

    • 0000minor version ,小版本号,比如版本是52.0,则minor version就是.0的概念
    • 0034major version ,大版本号,十进制是52,1.8编译完后就是52,1.9就是53。
  • 1110constant_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 格式 官网

a、javap 翻译class 文件 (java自带)

这样看不是很清楚,可以通过工具来很清晰的查看,就是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指令使用新语义

b、jclasslib 翻译class文件(idea插件)

使用 jclasslib 打开 class 文件,下面并进行分析和说明

i、一般信息

这里包含了class 文件结构的大多数基础信息,当然最重要的还是常量池。
在这里插入图片描述

  1. 本类索引,就是 this class :cp_info #7 <com/mashibing/jvm/c1_bytecode/T0100_ByteCode01>:后面的 <com/mashibing/jvm/c1_bytecode/T0100_ByteCode01> 就是本类的名称,cp_info #7 就是在常量池的7号存的。绿色可以点击,点击过去就是 常量池的7号位置。
  2. 父类索引,就是 super class:cp_info #2 <java/lang/Object> :后面的还是父类名称,前面就是父类存储在常量池的2号位置。
  3. 接口计数 即 interfaces count。
  4. 字段计数 即 fields count。
  5. 方法计数 即 methods count。
  6. 属性计数 即 attributes count。
ii、常量池:

在这里插入图片描述

  • 常量类型有很多种,如下图所示1,3,4,5,6到18,但是没有2,标记:常量池的每一种类型前边都有一个一个字节的标记,用的最多的是第一个:CONSTANT_Utf8_info ,代表 utf8的字符串。下图的思维导图可从文章开头的前言中找到并下载
    在这里插入图片描述
    在这里插入图片描述
  • 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。可以看出
    <init>: 是构造函数
    <()v>()是指 没有参数,V是指返回类型为void类型。
    在这里插入图片描述
iii、接口、字段

因为2.1测试小程序很简单,没有接口和字段,所以这里为空
在这里插入图片描述
在一般信息中,也可以看出个数,如下
在这里插入图片描述

iv、方法*****

测试小程序虽然很简单,啥都没有
但是生成一个默认的 构造函数,调用的父类也就是java.lang.Obeject的构造方法
这里也有最重要的code 环节,因为函数里会有code的哈,这里会有大量的指令集(JVM大约定义256个左右)
在这里插入图片描述

  • <init> 就是默认的构造函数,从指令的右边可以看出 调用的是父类的默认的构造方法。

  • code 就是重要的代码部分,右边的上面也有指向常量池的类名称等信息,从这里也可以看出,类方法信息也都存在了常量池中。

  • JVM 13 版本 的第七章,是十六进制码 与 指令的 对照。即 十六进制码 代表了什么意思,方法的具体实现翻译成class文件都变成了第七章(下图)的 十六进制(本质二进制)和指令(十六进制的映射),也就是 java的汇编语言
    在这里插入图片描述

  • 当前在字节码中,有三个 指令。
    指令的翻译可以从官网中查找对应的意思。

    1. 第一个是 aload_0通过上面文档可以找到(也可以通过鼠标在 jclasslib 中点进去如下图),
      在这里插入图片描述
      aload_0 对应的是 0X2a 这个十六进制,可以在class文件的十六进制文本中找到 2a 这个位置,这个位置对应的就是 aload_0 指令,那么 这个指令的作用是干嘛的呢。

      在文档中检索找到此位置,这个汇编指令代表的是:把本地变量表中的第0项(只要不是静态方法就是 this)放到栈里,然后执行第二条指令 invokespecial
      在这里插入图片描述

    2. invokespecial:可以看到右面的 #1 可以点进去进行跟踪。在文档找到描述,如下图,可以看到对应的16进制是 0Xb7。加载到栈中,然后是第三条指令,也就是最后一条指令。format中下面两个indexbyte1和indexbyte2 是指令的两个参数
      在这里插入图片描述

    3. return 返回指令,对应 b1

    4. 构造函数<init>的 三个指令 体现在十六进制的 class文件中如下所示。
      在这里插入图片描述

v、附加属性 attributes

这里默认的就是java的类名称
在这里插入图片描述

c、小总结*****

上面主要讲解了class文件的十六进制由JVM解释后的内容,通过JVM文档进行对应解释。

除了常量池之外的,存的内容都是常量池中的地址。

由常量池存放除常量池之外的其他内容的引用地址。
在这里插入图片描述

三、循序渐进增加内容,查看class文件

1、实现 interface 接口

a、测试小程序

package com.mashibing.jvm.c1_bytecode;

import java.io.Serializable;

public class T0101_ByteCode_With_Interfaces implements Cloneable, Serializable {
}

b、编译后class文件

//
// 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() {
    }
}

c、jclasslib 查看

多了两个实现的接口,可以看到存在了常量池
在这里插入图片描述
在这里插入图片描述

2、添加 函数

a、测试小程序

package com.mashibing.jvm.c1_bytecode;

public class T0102_ByteCode02 {
    void m() {
        int i=0;
        int j = i++;
    }
}

b、编译后class文件

package com.mashibing.jvm.c1_bytecode;

public class T0102_ByteCode02 {
    public T0102_ByteCode02() {
    }

    void m() {
        int i = 0;
        ++i;
    }
}

c、jclasslib 查看

可以看到 自定义方法和构造函数也都放到了常量池中。

  1. 增加了一个自定义方法。和默认的构造函数。
    在这里插入图片描述
  2. 自定义函数对应的指令。
    在这里插入图片描述

3、增加类属性

a、测试小程序

package com.mashibing.jvm.c1_bytecode;

public class T0103_ByteCode03 {
    int i = 0;
    String s = "Hello ByteCode!";
}

b、编译后的class文件

package com.mashibing.jvm.c1_bytecode;

public class T0103_ByteCode03 {
    int i = 0;
    String s = "Hello ByteCode!";

    public T0103_ByteCode03() {
    }
}

c、jclasslib 查看

类属性,即类成员变量,也存放在了常量池中。
在这里插入图片描述
在这里插入图片描述

4、增加带参构造函数和类属性

a、测试小程序

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;
    }
}

b、编译后的class文件

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;
    }
}

c、jclasslib 查看

在这里插入图片描述
在这里插入图片描述
红框中是函数参数类型和返回值类型。
在这里插入图片描述

5、增加构造函数、类方法、类属性

a、测试小程序

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() {}
}

b、编译后的class文件

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() {
    }
}

c、jclasslib 查看

在这里插入图片描述

在这里插入图片描述

四、如何找到 JVM class 格式 官网文档

1、直接进入网址即可

这是java 19版本的 JVM
https://docs.oracle.com/javase/specs/jvms/se19/html/index.html

2、从oracle 官网开始找

  1. 进入oracle 官网:https://www.oracle.com/,通过product 找到java 所在地进来。
    在这里插入图片描述
  2. 进入到了java页面,然后点击右上角的 download
    在这里插入图片描述
  3. 然后进入到了下载页面,也就是默认是最新的java版本,目前是19版本,如下所示
    在这里插入图片描述
  4. 拉到下面,找到 document download 中 下面的 read me,点进去
    在这里插入图片描述
  5. 然后找到如图的 jdk document 中的红框中的第一个,(这两个都有用)。
    在这里插入图片描述
  6. 这就是jdk 文档了,也可以从上面搜索任意一个版本的java版本。
    在这里插入图片描述
  7. 找到 下面的 specifications 中的 language and VM 这就是我们此行的目的地了
    在这里插入图片描述
  8. 这里就是各个版本的 jdk 文档了,同时还有每个版本新增的亮点,如下图所示,点击下面的 HTML。
    在这里插入图片描述
  9. 进入到文档(如下图所示),然后找到第四章(如下图所示),这就是 class 文件 格式的 内容了。
    在这里插入图片描述
    在这里插入图片描述

3、总结

从上面的最后一张截图所在的网址里,就可以学习 class 文件 格式的内容,也就是博文最开始总结的 xmind 的内容。
下图中就是class 文件 中 最重要的 常量池中的 tag 的标识。
在这里插入图片描述

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

java冯坚持

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值