欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JVM学习class字节码文件详解

程序员文章站 2022-03-16 14:54:02
class文件字节码结构class常量池如何存储数据class中的符号引用和直接引用(重点)class中的特殊字符串...

class文件字节码结构

我们可以用Hex Editor等工具或者在线工具online hex editor(https://www.onlinehexeditor.com/)打开一个class文件,查看里面内容,结果如下所示:
JVM学习class字节码文件详解
要阅读上面十六进制的class文件,就必须要了解class文件字节码的机构组织,如下图所示:
JVM学习class字节码文件详解

class常量池如何存储数据

常量池结构解析

JVM学习class字节码文件详解
cp_info:常量池项
constant_pool_count:常量池计数器

常量池项(cp_info)的结构

JVM学习class字节码文件详解
JVM虚拟机规定了不同的tag值和不同类型的字面量对应关系如下:
JVM学习class字节码文件详解
所以根据cp_info中的tag不同的值,可以将cp_info更细化为一下结构体(重点):
JVM学习class字节码文件详解
细化了的常量池的结构会是类似下图所示的样子:
JVM学习class字节码文件详解

int和float数据类型常量在常量池中的表示和存储

Java规定了int类型和float类型的数据类型占用4个字节的空间,其中int必须要添加final前缀才会将值放入常量池。
JVM学习class字节码文件详解

long和float数据类型常量在常量池中的表示和存储

long 类型和 double类型的数据类型占⽤8 个字节的空间。
JVM学习class字节码文件详解

String类型常量在常量池中的表示和存储

对字符串而言,JVM会将字符串类型的字面量(即双引号""括起的内容)以UTF-8编码格式存储到class字节码文件中。String类型在编译器编译时,会被转换成CONSTANT_String_info结构体,然后放置于常量池中。
JVM学习class字节码文件详解
CONSTANT_String_info结构体中的string_index的值指向了
CONSTANT_Utf8_info结构体,⽽字符串的utf-8编码数据就在这个结构体之中。
JVM学习class字节码文件详解
有如下实例代码

private String s1 = "JVM原理";

其在常量池中的存储如下图所示:
JVM学习class字节码文件详解

类文件中定义的类名和类中使用到的类在常量池中的表示和存储

JVM会将某个Java类中所有使用到了的类的完全限定名二进制形式的完全限定名封装成CONSTANT_Class_info结构体中,然后将其放入常量池里。
JVM学习class字节码文件详解
实例代码如下:

package com.jvm;
import java.util.Date;
public class ClassTest {
	private Date date =new Date();
}

其在常量池中的存储如下:
JVM学习class字节码文件详解
另外要注意:

  • 对于某个类或接口而言,其自身、父类和继承或实现的接口的信息会被直接组装成CONSTANT_Class_info常量池项放置到常量池中;
  • 类中或接⼝中使⽤到了其他的类,只有在类中实际使⽤到了该类时,该类的信息才会在常量池中有对应的CONSTANT_Class_info常量池项;
  • 类中或接⼝中仅仅定义某种类型的变量,JDK只会将变量的类型描述信息以UTF-8字符串组成CONSTANT_Utf8_info常量池项放置到常量池中,上⾯在类中的private Date date;JDK编译器只会将表示date的数据类型的“Ljava/util/Date”字符串放置到常量池中。

如何查看class文件中的常量池

实例代码如下:

public class App 
{
    private final int i1 = 10;
    private float f1 = 11f;
    private double d1 = 100d;
    private long l = 10L;
    Student stu1 = new Student();

    public static void main(String[] args) {
        System.out.println("Hello");
    }
}

可以在idea中通过插件来查看常量池内容。
插件名称:jclasslib Bytecode Viewer
使用方法:View --> Show Bytecode With Jclasslib
可通过该插件查看cp_info的内容,结果如下图所示:
JVM学习class字节码文件详解
在代码编译生成class后可通过命令javap -v XXX.class来查看,结果如下图所示:
JVM学习class字节码文件详解

哪些字面量会进入常量池中

  • final修饰的8中基本类型的值会进入常量池。
  • 非final类型的8中基本类型中,只有double、float和long的值会进入常量池。
  • 常量池中包含的字符串类型字面量(双引号""括起来的字符串值)

class中的符号引用和直接引用

符号引用(class文件中)

符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用能够无歧义的定位到目标即可。

符号引用于虚拟机的内存布局无关,引用的目标不一定加载到内存中。

在Java中,一个java类将会编译成一个class文件。在编译时,java类并不知道所引用的类的实际地址,因此只能使用符号引用来代替。

直接引用(运行时内存中)

直接引用可以是:

  • 直接指向目标的指针(比如,指向class对象、类变量、类方法的直接引用可能是指向方法区的指针)
  • 相对偏移量(比如,指向实例变量、实例方法的直接引用都是偏移量)
  • 一个能间接定位到目标的句柄。

直接引用是和虚拟机的布局相关的,同一个符号引用在不同的虚拟机实例上翻译出来的直接引用一般不会相同。如果有了直接引用,那引用的目标必定已经被加载入内存中了。

引用替换的时机

符号引用替换为直接引用的操作发生在类加载过程中的解析阶段,会将符号引用转换为对应的直接引用,放入运行时常量池中。

class中的特殊字符串

特殊字符串包括三种:类的全限定名、字段和方法的描述符、特殊方法的方法名。

类的全限定名

源文件中一个类的名字,在class文件中是用全限定名表述的。

例如,Object类,在源⽂件中的全限定名是java.lang.Object 。⽽class⽂件中的全限定名是将点号替换成“/” , 也就是java/lang/Object 。

描述符

待补充

特殊方法的方法名

这里的特殊方法是指的类的构造方法和类型的初始化方法。
构造⽅法就不⽤多说了, ⾄于类型的初始化⽅法, 对应到源码中就是静态初始化块。 也就是说, 静态初始化块, 在class⽂件中是以⼀个⽅法表述的, 这个⽅法同样有⽅法描述符和⽅法名,具体如下:

  • 类的构造⽅法的⽅法名使⽤字符串<init>表示
  • 静态初始化⽅法的⽅法名使⽤字符串<clinit>表示
  • 除了这两种特殊的⽅法外,其他普通⽅法的⽅法名, 和源⽂件中的⽅法名相同

本文地址:https://blog.csdn.net/code_way/article/details/109906248