java虚拟机原理:Class字节码二进制文件分析

 更新时间:2021年09月09日 09:12:47   作者:韩曙亮  
class文件全名称为Java class文件,主要在平台无关性和网络移动性方面使Java更适合网络。它在平台无关性方面的任务是:为Java程序提供独立于底层主机平台的二进制形式的服务。下面我们来详细解读下它吧

一、字节码文件 与 JVM

Java 源码编译成 Class 字节码 ;

Java 虚拟机 可以被认为是一个 解释器 , 解释编译后的 Class 字节码文件 , 最后在不同的操作系统中运行 ;

Android 虚拟机 不是 Java 规范的 虚拟机 , 有一些根据嵌入式设备进行的定制的实现 ;

Class 字节码 本质上就是 二进制数据 , 运行时 , 会被 类加载器 加载到 Java 虚拟机内存的 方法区 中 ; 同时 创建 Class 对象 ;

( Java 虚拟机内存分为 : 堆区 , 方法区 , 栈 , 本地方法栈 , 程序计数器 )

由于要将 Class 字节码文件 加载到 JVM 内存的 方法区 中 , 要占用一定的内存空间 , 这里要求 Class 字节码文件 , 越小越好 ;

二、字节码文件示例

Java 源代码如下 :

public class Student {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

使用 javac 命令将 Student.java 源码编译成 Student.class字节码文件 :

javac Student.java

字节码文件二进制数据分析 :

使用二进制查看工具查看 Student.class 字节码文件 , 这些二进制数值对应的就是 JVM 指令 ;

在这里插入图片描述

CA FE BA BE 00 00 00 34 00 15 0A 00 04 00 11 09
00 03 00 12 07 00 13 07 00 14 01 00 04 6E 61 6D
65 01 00 12 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53
74 72 69 6E 67 3B 01 00 06 3C 69 6E 69 74 3E 01
00 03 28 29 56 01 00 04 43 6F 64 65 01 00 0F 4C
69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65 01 00
07 67 65 74 4E 61 6D 65 01 00 14 28 29 4C 6A 61
76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 01
00 07 73 65 74 4E 61 6D 65 01 00 15 28 4C 6A 61
76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 29
56 01 00 0A 53 6F 75 72 63 65 46 69 6C 65 01 00
0C 53 74 75 64 65 6E 74 2E 6A 61 76 61 0C 00 07
00 08 0C 00 05 00 06 01 00 07 53 74 75 64 65 6E
74 01 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62
6A 65 63 74 00 21 00 03 00 04 00 00 00 01 00 02
00 05 00 06 00 00 00 03 00 01 00 07 00 08 00 01
00 09 00 00 00 1D 00 01 00 01 00 00 00 05 2A B7
00 01 B1 00 00 00 01 00 0A 00 00 00 06 00 01 00
00 00 01 00 01 00 0B 00 0C 00 01 00 09 00 00 00
1D 00 01 00 01 00 00 00 05 2A B4 00 02 B0 00 00
00 01 00 0A 00 00 00 06 00 01 00 00 00 05 00 01
00 0D 00 0E 00 01 00 09 00 00 00 22 00 02 00 02
00 00 00 06 2A 2B B5 00 02 B1 00 00 00 01 00 0A
00 00 00 0A 00 02 00 00 00 09 00 05 00 0A 00 01
00 0F 00 00 00 02 00 10

使用

javap -v Student.class

命令 , 生成上述字节码文件的 附加信息 ;

命令行输出 :

D:\jvm>javap -v Student.class
Classfile /D:/jvm/Student.class
  Last modified 2021-9-4; size 392 bytes
  MD5 checksum 8b9bb897bb8cf2a8addf04be5b7b915f
  Compiled from "Student.java"
public class Student
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #4.#17         // java/lang/Object."<init>":()V
   #2 = Fieldref           #3.#18         // Student.name:Ljava/lang/String;
   #3 = Class              #19            // Student
   #4 = Class              #20            // java/lang/Object
   #5 = Utf8               name
   #6 = Utf8               Ljava/lang/String;
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               getName
  #12 = Utf8               ()Ljava/lang/String;
  #13 = Utf8               setName
  #14 = Utf8               (Ljava/lang/String;)V
  #15 = Utf8               SourceFile
  #16 = Utf8               Student.java
  #17 = NameAndType        #7:#8          // "<init>":()V
  #18 = NameAndType        #5:#6          // name:Ljava/lang/String;
  #19 = Utf8               Student
  #20 = Utf8               java/lang/Object
{
  public Student();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0
  public java.lang.String getName();
    descriptor: ()Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #2                  // Field name:Ljava/lang/String;
         4: areturn
      LineNumberTable:
        line 5: 0
  public void setName(java.lang.String);
    descriptor: (Ljava/lang/String;)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: putfield      #2                  // Field name:Ljava/lang/String;
         5: return
      LineNumberTable:
        line 9: 0
        line 10: 5
}
SourceFile: "Student.java"

在这里插入图片描述

下面开始逐个字节解析上述字节码文件 ;

三、字节码文件二进制结构分析

分析字节码二进制文件时 , 可以参考 javap -v Student.class 命令输出的字节码附加信息进行理解 ;

1、魔数

magic ( 魔数 ) : 4 4 4 字节 , CA FE BA BE , 所有的 Class 字节码都是以 CafeBabe 信息开头的 ;

在这里插入图片描述

2、次版本号

minor_version ( 次版本号 ) : 2 2 2 字节 , 00 00 , 次版本号是 0 0 0 ; 对应字节码附加信息中的 minor version: 0 ;

在这里插入图片描述

3、主版本号

major_version ( 主版本号 ) : 2 2 2 字节 , 00 34 , 主版本号是 52 52 52 ; 对应字节码附加信息中的 major version: 52 ;

  • 这个主版本号 52 对应 JDK 版本的 1.8 版本 ;
  • 51 对应 1.7 ;
  • 53 对应 1.9 ;
  • 45 对应 1.0 ;

在这里插入图片描述

4、常量池个数

constant_pool_count ( 常量池个数 ) : 2 2 2 字节 , 00 15 , 常量池个数是 21 21 21 个 ; 由于 JVM 占用了默认的常量池 #0 , 因此实际上的常量个数是 21 − 1 21 - 1 21−1 个 , 需要对这个数减一处理 ;

在这里插入图片描述

字节码附加信息中 常量池参考 , 有 20 20 20 个常量池 ; #0 常量池 , 被 JVM 占用了 , 代表了一个空引用 , 不指向任何位置 ;

Constant pool:
   #1 = Methodref          #4.#17         // java/lang/Object."<init>":()V
   #2 = Fieldref           #3.#18         // Student.name:Ljava/lang/String;
   #3 = Class              #19            // Student
   #4 = Class              #20            // java/lang/Object
   #5 = Utf8               name
   #6 = Utf8               Ljava/lang/String;
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               getName
  #12 = Utf8               ()Ljava/lang/String;
  #13 = Utf8               setName
  #14 = Utf8               (Ljava/lang/String;)V
  #15 = Utf8               SourceFile
  #16 = Utf8               Student.java
  #17 = NameAndType        #7:#8          // "<init>":()V
  #18 = NameAndType        #5:#6          // name:Ljava/lang/String;
  #19 = Utf8               Student
  #20 = Utf8               java/lang/Object

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注脚本之家的更多内容!

相关文章

  • redis之基于SpringBoot实现Redis stream实时流事件处理方式

    redis之基于SpringBoot实现Redis stream实时流事件处理方式

    这篇文章主要介绍了redis之基于SpringBoot实现Redis stream实时流事件处理方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-06-06
  • Java面试之动态规划与组合数

    Java面试之动态规划与组合数

    这篇文章主要介绍了Java面试之动态规划与组合数的相关知识,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-09-09
  • java实现鲜花销售系统

    java实现鲜花销售系统

    这篇文章主要为大家详细介绍了java实现鲜花销售系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-06-06
  • Java16 JDK安装并设置环境变量的方法步骤

    Java16 JDK安装并设置环境变量的方法步骤

    突然想起自己大学刚接触java的时候,要下载JDK和配置环境变量,那时候我上网找了很多教学,本文就详细的介绍一下Java16 JDK安装并设置环境变量,感兴趣的可以了解一下
    2021-09-09
  • Java包含抽象方法的枚举类示例

    Java包含抽象方法的枚举类示例

    这篇文章主要介绍了Java包含抽象方法的枚举类,结合实例形式分析了Java包含抽象方法的枚举类实现方法与相关操作技巧,需要的朋友可以参考下
    2019-08-08
  • mybatis使用Integer类型查询可能出现的问题

    mybatis使用Integer类型查询可能出现的问题

    这篇文章主要介绍了mybatis使用Integer类型查询可能出现的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • 合成聚合复用原则_动力节点Java学院整理

    合成聚合复用原则_动力节点Java学院整理

    这篇文章主要介绍了合成聚合复用原则,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • Java之Algorithm_analysis案例详解

    Java之Algorithm_analysis案例详解

    这篇文章主要介绍了Java之Algorithm_analysis案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-09-09
  • java实现微信App支付服务端

    java实现微信App支付服务端

    这篇文章主要为大家详细介绍了java实现微信App支付服务端,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-10-10
  • Spring多对象引入方法

    Spring多对象引入方法

    今天小编就为大家分享一篇关于Spring多对象引入方法,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-01-01

最新评论