JVM-类加载过程

前置知识

类的生命周期:
    1.加载
    2.连接
        2.1验证
        2.2准备
        2.3解析
    3.初始化
    4.使用
    5.卸载

类加载过程

类加载过程三大步骤:
    1.加载
    2.连接
        2.1验证
        2.2准备
        2.3解析
    3.初始化

加载

类加载过程第一步,加载,主要干了三件事:
1.通过全类名获取该类的字节码文件,并将其加载成二进制字节流
2.将字节流所代表的静态存储结构(编码结构)转换为方法区的运行时数据结构(运行时的内存结构)
3.在内存中生成一个代表该类的 Class 对象,作为方法区这些数据的访问入口
package com.tan.classloading;
//静态存储结构
public class Cola {
    private int price =2;
    private static int capacity = 330;
    static {
        int invent_date = 18860508;
    }
    public Cola(){
        price = 3;
        price = 5;
    }
}
//通过查看字节码文件,观察运行时的内存结构
Classfile /D:/220106/code/Jvm/target/classes/com/tan/classloading/Cola.class
  Last modified 2022-6-10; size 449 bytes
  MD5 checksum e6d9a0cd5658f8b7f42c72225c62fbbe
  Compiled from "Cola.java"
public class com.tan.classloading.Cola
  minor version: 0
  major version: 49
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#20         // java/lang/Object."<init>":()V
   #2 = Fieldref           #5.#21         // com/tan/classloading/Cola.price:I
   #3 = Fieldref           #5.#22         // com/tan/classloading/Cola.capacity:I
   #4 = Integer            18860508
   #5 = Class              #23            // com/tan/classloading/Cola
   #6 = Class              #24            // java/lang/Object
   #7 = Utf8               price
   #8 = Utf8               I
   #9 = Utf8               capacity
  #10 = Utf8               <init>
  #11 = Utf8               ()V
  #12 = Utf8               Code
  #13 = Utf8               LineNumberTable
  #14 = Utf8               LocalVariableTable
  #15 = Utf8               this
  #16 = Utf8               Lcom/tan/classloading/Cola;
  #17 = Utf8               <clinit>
  #18 = Utf8               SourceFile
  #19 = Utf8               Cola.java
  #20 = NameAndType        #10:#11        // "<init>":()V
  #21 = NameAndType        #7:#8          // price:I
  #22 = NameAndType        #9:#8          // capacity:I
  #23 = Utf8               com/tan/classloading/Cola
  #24 = Utf8               java/lang/Object
{
  public com.tan.classloading.Cola();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: iconst_2
         6: putfield      #2                  // Field price:I
         9: aload_0
        10: iconst_3
        11: putfield      #2                  // Field price:I
        14: aload_0
        15: iconst_5
        16: putfield      #2                  // Field price:I
        19: return
      LineNumberTable:
        line 9: 0
        line 4: 4
        line 10: 9
        line 11: 14
        line 12: 19
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      20     0  this   Lcom/tan/classloading/Cola;

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=1, locals=1, args_size=0
         0: sipush        330
         3: putstatic     #3                  // Field capacity:I
         6: ldc           #4                  // int 18860508
         8: istore_0
         9: return
      LineNumberTable:
        line 5: 0
        line 7: 6
        line 8: 9
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
}
SourceFile: "Cola.java"
怎么通过字节码文件查看运行时的内存结构?
首先点击Build-Build project,编译该类,去生成该类对应的字节码文件
其次在target文件夹下找到该字节码文件,右键选择open in terminal
最后在terminal中输入命令:

在命令行中显示:
javap -v Cola.class
将该字节码文件存储到指定文件中:
javap -v Cola.class >a.txt


为了更好的观察运行时的内存结构,可以在idea中下载插件:
jclasslib Bytecode Viewer

怎么使用jclasslib Bytecode Viewer去查看字节码文件的运行时的内存结构?
点击View-show bytecode with jclasslib

连接

类加载过程第二步,连接,又可以细化为三个步骤:
1.验证
	目的在于确保c1ass文件的字节流中包含信息符合当前虚拟机要求,保证被加载类的正确性,不会危害虚拟机自身安全
	主要包括四种验证,文件格式验证,元数据验证,字节码验证,符号引用验证
		-->进行各种验证,进而保证正确加载类
	
2.准备
	为类变量分配内存并且设置该类变量的默认初始值,即零值
	这里不包含用final修饰的static,因为final在编译的时候就分配了,准备阶段会显式初始化
	这里不会为实例变量分配初始化,类变量会分配在方法区中,而实例变量是会随着对象一起分配到Java堆中
		-->为类变量(static修饰的变量,不包括final static修饰的,因为final在编译的时候就分配了)分配内存,并且设置类变量的默认初始值

3.解析
	将常量池内的符号引用转换为直接用的过程
	事实上,解析换作往往会伴随着M在执行完初始化之后再执行
	符号引用就是一组符号来描述所引用的目标。符号引用的字面量形式明确定义在《Java虚拟机机范》的c1ss文件格式中。直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄
	解析动作主要针对类或接口、字段、类方法、接口方法、方法类型等。对应常量池中的CONSTANT_Class_info,CONSTANT_Fieldref_info,CONSTANT_Methodref_info等
		-->将Java类解析成各种成分:常量池、接口、字段、方法、属性等


注意:
加载阶段和连接阶段不是严格的前后执行关系,加载阶段和连接阶段的部分内容是交叉进行的,加载阶段可能还未结束,连接阶段可能就已经开始了

初始化

类加载过程第三步,初始化:
	初始化阶段就是执行类构造器方法<c11n1t()的过程
	此方法不需定义,是javac编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来
	构造器方法中指令按语句在源文件中出现的顺序执行
	<c11n1t>()不同于类的构造器。(关联:构造器是虚拟机视角下的<init>())
	若该类具有父类,JVM会保证子类的<c1init>()执行前,父类的<c1init>()已经执行完毕
	虚拟机必须保证一个类的<c1init>()方法在多线程下被同步加锁
		-->执行类构造方法<init>和初始化方法<clinit>的过程
		--><clinit>不是构造器,其主要汇聚所有静态变量赋值和静态代码块,并为其赋值

注意:
<clinit> 方法在编译之后会自动生成,并且<clinit>方法是带锁线程安全,在多线程环境下进行类初始化可能会导致多个进程阻塞

JVM规定的六种必须对类进行初始化的场景:
1.遇到 new(创建对象) 、 getstatic(读取为被final修饰的类的静态变量)、putstatic(给类的静态变量赋值) 或 invokestatic(调用一个类中的静态方法) 这 4 条直接码指令的时候
    e.g:创建对象

2.使用 java.lang.reflect 包的方法对类进行反射调用
	e.g:Class.forname("xxx")、newInstance() 

3.初始化一个类,其继承了一个类,并且其父类还未初始化,则会先触发该父类的初始化

4.当虚拟机开始运行,虚拟机会先初始化要执行的主类 (程序入口)

5.要使用MethodHandle 和 VarHandle (轻量级的反射调用机制),就必须先使用 findStaticVarHandle 来初始化要调用的类

6.当一个接口中定义了 JDK8 新加入的默认方法,即:被 default 关键字修饰的接口方法,如果该接口的实现类发生了初始化,那该接口必须要在其之前被初始化
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值