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

简介: 这篇文章是关于JVM基础知识的介绍,包括JVM的跨平台和跨语言特性、Class文件格式的详细解析,以及如何使用javap和jclasslib工具来分析Class文件。

前言

  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月前
|
XML Java 编译器
Java学习十六—掌握注解:让编程更简单
Java 注解(Annotation)是一种特殊的语法结构,可以在代码中嵌入元数据。它们不直接影响代码的运行,但可以通过工具和框架提供额外的信息,帮助在编译、部署或运行时进行处理。
95 43
Java学习十六—掌握注解:让编程更简单
|
16天前
|
Arthas Prometheus 监控
监控堆外使用JVM工具
监控堆外使用JVM工具
29 7
|
14天前
|
Oracle 安全 Java
深入理解Java生态:JDK与JVM的区分与协作
Java作为一种广泛使用的编程语言,其生态中有两个核心组件:JDK(Java Development Kit)和JVM(Java Virtual Machine)。本文将深入探讨这两个组件的区别、联系以及它们在Java开发和运行中的作用。
36 1
|
1月前
|
Java 大数据 API
14天Java基础学习——第1天:Java入门和环境搭建
本文介绍了Java的基础知识,包括Java的简介、历史和应用领域。详细讲解了如何安装JDK并配置环境变量,以及如何使用IntelliJ IDEA创建和运行Java项目。通过示例代码“HelloWorld.java”,展示了从编写到运行的全过程。适合初学者快速入门Java编程。
|
1月前
|
JavaScript Java 项目管理
Java毕设学习 基于SpringBoot + Vue 的医院管理系统 持续给大家寻找Java毕设学习项目(附源码)
基于SpringBoot + Vue的医院管理系统,涵盖医院、患者、挂号、药物、检查、病床、排班管理和数据分析等功能。开发工具为IDEA和HBuilder X,环境需配置jdk8、Node.js14、MySQL8。文末提供源码下载链接。
|
2月前
|
Arthas 监控 数据可视化
JVM进阶调优系列(7)JVM调优监控必备命令、工具集合|实用干货
本文介绍了JVM调优监控命令及其应用,包括JDK自带工具如jps、jinfo、jstat、jstack、jmap、jhat等,以及第三方工具如Arthas、GCeasy、MAT、GCViewer等。通过这些工具,可以有效监控和优化JVM性能,解决内存泄漏、线程死锁等问题,提高系统稳定性。文章还提供了详细的命令示例和应用场景,帮助读者更好地理解和使用这些工具。
|
18天前
|
缓存 Prometheus 监控
Elasticsearch集群JVM调优设置合适的堆内存大小
Elasticsearch集群JVM调优设置合适的堆内存大小
154 1
|
2月前
|
存储 安全 Java
jvm 锁的 膨胀过程?锁内存怎么变化的
【10月更文挑战第3天】在Java虚拟机(JVM)中,`synchronized`关键字用于实现同步,确保多个线程在访问共享资源时的一致性和线程安全。JVM对`synchronized`进行了优化,以适应不同的竞争场景,这种优化主要体现在锁的膨胀过程,即从偏向锁到轻量级锁,再到重量级锁的转变。下面我们将详细介绍这一过程以及锁在内存中的变化。
40 4
|
7天前
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。
|
16天前
|
Java
JVM内存参数
-Xmx[]:堆空间最大内存 -Xms[]:堆空间最小内存,一般设置成跟堆空间最大内存一样的 -Xmn[]:新生代的最大内存 -xx[use 垃圾回收器名称]:指定垃圾回收器 -xss:设置单个线程栈大小 一般设堆空间为最大可用物理地址的百分之80