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

目录
  • 一、字节码文件 与 JVM
  • 二、字节码文件示例
  • 三、字节码文件二进制结构分析
    • 1、魔数
    • 2、次版本号
    • 3、主版本号
    • 4、常量池个数
  • 总结

一、字节码文件 与 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

总结

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

(0)

相关推荐

  • 深入理解Java虚拟机 JVM 内存结构

    目录 前言 JVM是什么 JVM内存结构概览 运行时数据区 程序计数器 Java虚拟机栈 本地方法栈 方法区 运行时常量池 Java堆 直接内存 前言 JVM是Java中比较难理解和掌握的一部分,也是面试中被问的比较多的,掌握好JVM底层原理有助于我们在开发中写出效率更高的代码,可以让我们面对OutOfMemoryError时不再一脸懵逼,可以用掌握的JVM知识去查找分析问题.去进行JVM的调优.去让我们的应用程序可以支持更高的并发量等......总之一句话,学好JVM很重要! JVM是什么 J

  • java虚拟机JVM类加载机制原理(面试必问)

    目录 1.类加载的过程. 1)加载 2)验证 3)准备 4)解析 5)初始化 2.Java 虚拟机中有哪些类加载器? 1)启动类加载器(Bootstrap ClassLoader): 2)扩展类加载器(Extension ClassLoader): 3)应用程序类加载器(Application ClassLoader): 3.什么是双亲委派模型? 4.为什么使用双亲委派模式? 5.有哪些场景破坏了双亲委派模型? 1)线程上下文类加载器 2)Tomcat 的多 Web 应用程序 3)OSGI 实现

  • java虚拟机原理:类加载过程详解

    目录 一.Java 类加载过程 1.字节码编译 2.加载 3.连接 4.初始化 总结 一.Java 类加载过程 1.字节码编译 编写好 Java 源码 Student.java , 使用 javac 将上述 Java 源码编译成 Class 字节码文件 Student.class , 2.加载 加载 : 通过 " 类加载子系统 " 将该字节码文件 , 加载到 Java 虚拟机内存中 的 方法区 , 然后开始执行 " 连接 " 操作 , 类加载时机 : Java 程序

  • java内存模型jvm虚拟机简要分析

    目录 主内存和工作内存 内存间的交互操作 原子性.可见性.有序性 原子性 可见性 有序性 主内存和工作内存 Java 内存模型规定了所有的变量都存储在主内存中, 每条线程有自己的工作内存 线程的工作内存中保存了被该线程使用的变量的主内存副本, 线程对变量的所有操作 (读取.赋值等) 都必须在工作内存中进行, 而不能直接读写主内存中的数据 不同的线程之间也无法直接访问对方工作内存中的变量, 线程间变量值的传递均需要通过主内存来完成 内存间的交互操作 原子性.可见性.有序性 Java 内存模型是围绕

  • Java虚拟机JVM类加载机制(从类文件到虚拟机)

    一.类加载机制简介 什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构.类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口. 类加载机制:所谓的类加载机制就是虚拟机将class文件加载到内存,并对数据进行验证,转换解析和初始化,形成虚拟机可以直接使用的jav

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

    目录 一.字节码文件 与 JVM 二.字节码文件示例 三.字节码文件二进制结构分析 1.魔数 2.次版本号 3.主版本号 4.常量池个数 总结 一.字节码文件 与 JVM Java 源码编译成 Class 字节码 ; Java 虚拟机 可以被认为是一个 解释器 , 解释编译后的 Class 字节码文件 , 最后在不同的操作系统中运行 ; Android 虚拟机 不是 Java 规范的 虚拟机 , 有一些根据嵌入式设备进行的定制的实现 ; Class 字节码 本质上就是 二进制数据 , 运行时 ,

  • Java ThreadLocal原理解析以及应用场景分析案例详解

    目录 ThreadLocal的定义 ThreadLocal的应用场景 ThreadLocal的demo TheadLocal的源码解析 ThreadLocal的set方法 ThreadLocal的get方法 ThreadLocalMap的结构 ThreadLocalMap的set方法 ThreadLocalMap的getEntry方法 ThreadLocal的内存泄露 如何避免内存泄露呢 应用实例 实际应用二 总结 ThreadLocal的定义 JDK对ThreadLocal的定义如下: The

  • java锁机制ReentrantLock源码实例分析

    目录 一:简述 二:ReentrantLock类图 三:流程简图 四:源码分析 lock()源码分析: 非公平实现: 公平锁实现: tryAcquire()方法 公平锁实现: 非公平锁实现: addWaiter() acquireQueued() shouldParkAfterFailedAcquire() parkAndCheckInterrupt() unlock()方法源码分析: tryRelease() unparkSuccessor() 五:总结 一:简述 ReentrantLock是

  • Java类加载器ClassLoader源码层面分析讲解

    目录 Launcher 源码 AppClassLoader 源码 ExtClassLoader 源码 ClassLoader 源码 总结 最终总结一下 Launcher 源码 sun.misc.Launcher类是java 虚拟机的入口,在启动 java应用 的时候会首先创建Launcher.在初始化Launcher对象的时候会创建一个ExtClassLoader拓展程序加载器 和 AppClassLoader应用程序类加载器(这俩鬼东西好像只是加载类的路径不一样而已),然后由这俩类加载器去加载

  • Java Agent 动态修改字节码详情

    目录 1.什么是Java Agent 2.实现Java Agent 2.1 类转换 2.2 使用Java代理进行实际操作 假设您有一个在生产环境中运行的应用程序.每隔一段时间,它就会进入中断状态,错误很难重现,您需要从应用程序中获得更多信息. 那么你想知道解决方案吗? 您可以做的是动态地将一些代码集附加到应用程序中,并仔细地重写它,以便代码转储您可以记录的其他信息,或者您可以将应用程序阶段转储到文本文件中.Java为我们提供了使用Java Agent实现这一点的工具. 你有没有想过我们的Java

  • Java面试题 从源码角度分析HashSet实现原理

    面试官:请问HashSet有哪些特点? 应聘者:HashSet实现自set接口,set集合中元素无序且不能重复: 面试官:那么HashSet 如何保证元素不重复? 应聘者:因为HashSet底层是基于HashMap实现的,当你new一个HashSet时候,实际上是new了一个map,执行add方法时,实际上调用map的put方法,value始终是PRESENT,所以根据HashMap的一个特性: 将一个key-value对放入HashMap中时,首先根据key的hashCode()返回值决定该E

  • java获取版本号及字节码编译版本方法示例

    前言 之所以会有这篇文章,是因为公司的开发环境比较老,寻找一些jar包的时候总是会纠结对应的编译版本,感觉很麻烦,所以写了一个工具类用于读取class或jar文件的编译版本,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 示例代码 package com.jinggujin.util; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.

  • JAVA面试题 从源码角度分析StringBuffer和StringBuilder的区别

    面试官:请问StringBuffer和StringBuilder有什么区别? 这是一个老生常谈的话题,笔者前几年每次面试都会被问到,作为基础面试题,被问到的概率百分之八九十.下面我们从面试需要答到的几个知识点来总结一下两者的区别有哪些? 继承关系? 如何实现的扩容? 线程安全性? 继承关系 从源码上看看类StringBuffer和StringBuilder的继承结构: 从结构图上可以直到,StringBuffer和StringBuiler都继承自AbstractStringBuilder类 如何

  • Java JVM字节码指令集总结整理与介绍

    目录 Java是怎么跨平台的 平台无关的基石 JVM字节码指令介绍 字节码与数据类型 加载和存储指令 算术指令 类型转换指令 宽化类型转换 窄化类型转换: 对象创建与访问指令 操作数栈管理指令 控制转移指令 方法调用和返回指令 异常处理指令 同步指令 JVM指令集大全 Java是怎么跨平台的 我们上计算机课的时候老师讲过:"计算机只能识别0和1,所以我们写的程序要经过编译器翻译成0和1组成的二进制格式计算机才能执行".我们编译后产生的.class文件是二进制的字节码,字节码是不能被机器

随机推荐