类加载的过程

  1. 加载
  2. 验证
  3. 准备
  4. 解析
  5. 初始化

加载

  • 将类的.class文件中的二进制数据读入到内存中
  • 将其放在运行时数据区的方法区内
  • 然后再内存中创建一个java.lang.Class对象用来封装类在方法区内的数据结构

简单地说,加载:在硬盘上查找并通过IO读入字节码文件

验证

目的:确保Class文件字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
主要分为四个检验部分:

  1. 文件格式验证:检验是否为class文件
  2. 元数据验证:验证是否符合Java语言规范
  3. 字节码验证:检验程序是否合法,符合逻辑
  4. 符号引用验证:保证该引用能够被访问到

要注意,验证阶段是非常重要的,但并不是一定必要的,对于反复使用和验证过的代码,可以通过使用-Xverify:none来关闭大部分类验证,来缩短验证时间。

准备

为类变量分配内存,设置类变量初始值

要注意的是:

  • 内存分配仅包括类变量(static),不包括实例变量,实例变量会在对象实例化时随对象分配在Java堆中
  • 初始值在通常情况下是0,并不是程序里的那个值。真正的赋值是在初始化时完成的。

    解析

    符号引用替换为直接引用,该阶段会把一些静态方法替换为指向数据所存内存的指针或句柄等(直接引用),这是所谓的静态链接的过程(程序加载期间完成),动态链接时在程序运行期间完成的符号引用替换为直接引用

符号引用:用符号来表示目标,符号可以是任意的字面量,要求无歧义

我们以字节码文档为例:

将一个简单的代码用javap命令生成字节码文件,内容如下

package Test;

public class Test {
    public int computer(){
        int a=1;
        int b=2;
        return a+b;
    }
    public static void main(String[] args) {
        Test test = new Test();
        int c = test.computer();
        System.out.println(c);
    }
}

```

Classfile /C:/Users/董润泽/workspace/Java虚拟机/src/Test/Test.class
  Last modified 2020-1-21; size 490 bytes
  MD5 checksum 1015aa0bd5fd4af85cc23a21ef42a1af
  Compiled from "Test.java"
public class Test.Test
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #7.#18         // java/lang/Object."<init>":()V
   #2 = Class              #19            // Test/Test
   #3 = Methodref          #2.#18         // Test/Test."<init>":()V
   #4 = Methodref          #2.#20         // Test/Test.computer:()I
   #5 = Fieldref           #21.#22        // java/lang/System.out:Ljava/io/PrintStream;
   #6 = Methodref          #23.#24        // java/io/PrintStream.println:(I)V
   #7 = Class              #25            // java/lang/Object
   #8 = Utf8               <init>
   #9 = Utf8               ()V
  #10 = Utf8               Code
  #11 = Utf8               LineNumberTable
  #12 = Utf8               computer
  #13 = Utf8               ()I
  #14 = Utf8               main
  #15 = Utf8               ([Ljava/lang/String;)V
  #16 = Utf8               SourceFile
  #17 = Utf8               Test.java
  #18 = NameAndType        #8:#9          // "<init>":()V
  #19 = Utf8               Test/Test
  #20 = NameAndType        #12:#13        // computer:()I
  #21 = Class              #26            // java/lang/System
  #22 = NameAndType        #27:#28        // out:Ljava/io/PrintStream;
  #23 = Class              #29            // java/io/PrintStream
  #24 = NameAndType        #30:#31        // println:(I)V
  #25 = Utf8               java/lang/Object
  #26 = Utf8               java/lang/System
  #27 = Utf8               out
  #28 = Utf8               Ljava/io/PrintStream;
  #29 = Utf8               java/io/PrintStream
  #30 = Utf8               println
  #31 = Utf8               (I)V
{
  public Test.Test();
    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 3: 0

  public int computer();
    descriptor: ()I
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=1
         0: iconst_1
         1: istore_1
         2: iconst_2
         3: istore_2
         4: iload_1
         5: iload_2
         6: iadd
         7: ireturn
      LineNumberTable:
        line 5: 0
        line 6: 2
        line 7: 4

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: new           #2                  // class Test/Test
         3: dup
         4: invokespecial #3                  // Method "<init>":()V
         7: astore_1
         8: aload_1
         9: invokevirtual #4                  // Method computer:()I
        12: istore_2
        13: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
        16: iload_2
        17: invokevirtual #6                  // Method java/io/PrintStream.println:(I)V
        20: return
      LineNumberTable:
        line 10: 0
        line 11: 8
        line 12: 13
        line 13: 20
}
SourceFile: "Test.java"

可以看到,上面有一些“#数字”:

这其实就是生成的符号,对于后面的解释,例如“#7:#8”即指“#1”这个符号引用了“#7”和“#8”的内容

这即为符号引用

直接引用:引用的是目标的一个地址

初始化

对类的静态变量初始化为指定的值,执行静态代码块

同准备阶段的赋初始值不同!

在博客 类加载的时机已介绍,可参考理解。


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 2470290795@qq.com

文章标题:类加载的过程

文章字数:1.1k

本文作者:runze

发布时间:2020-01-28, 16:46:14

最后更新:2020-01-28, 19:26:41

原始链接:http://yoursite.com/2020/01/28/JVM/JVM%E7%B1%BB%E5%8A%A0%E8%BD%BD%E6%9C%BA%E5%88%B62%E2%80%94%E2%80%94%E7%B1%BB%E5%8A%A0%E8%BD%BD%E7%9A%84%E8%BF%87%E7%A8%8B/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏