JVM类加载(类加载过程、双亲委派模型)

简介: JVM类加载(类加载过程、双亲委派模型)

一、类加载过程

对于一个类来说,它的生命周期是这样的:

1.加载

加载Loading)阶段是整个类加载Class Loading)过程中的一个阶段,它和类加载Class

Loading 是不同的,一个是加载 Loading 另一个是类加载 Class Loading ,所以不要把二者搞混了。

在加载 Loading 阶段, Java 虚拟机需要完成以下三件事情:

1 )通过一个类的全限定名来获取定义此类的二进制字节流。

2 )将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。

3 )在内存中生成一个代表这个类的 java.lang.Class 对象,作为方法区这个类的各种数据的访问入口

2.验证

验证是连接阶段的第一步,这一阶段的目的是确保Class文件的字节 流中包含的信息符合《Java虚拟机规范》的全部约束要求,保证这些信 息被当作代码运行后不会危害虚拟机自身的安全。

验证选项:

文件格式验证

字节码验证

符号引用验证 ...

3.准备

准备阶段是正式为类中定义的变量(即静态变量,被 static 修饰的变量)分配内存并设置类变量初始值的阶段。

比如此时有这样一行代码:

public static int value = 123;
//它是初始化 value 的 int 值为 0,而非 123。

4.解析

解析阶段是Java 虚拟机将常量池内的符号引用替换为直接引用的过程,也就是初始化常量的过程。

5.初始化

初始化阶段,Java 虚拟机真正开始执行类中编写的Java 程序代码,将主导权移交给应用程序。初始化阶段就是执行类构造器方法的过程。


二、关于类加载的典型试题

这段代码的输出结果是什么?

class A{
    public A(){
        System.out.println("A的构造方法");
    }
    {
        System.out.println("A的构造代码块");
    }
    static {
        System.out.println("A的静态代码块");
    }
}
class B extends A{
    public B(){
        System.out.println("B的构造方法");
    }
    {
        System.out.println("B的构造代码块");
    }
    static {
        System.out.println("B的静态代码块");
    }
}
public class test extends B{
    public static void main(String[] args) {
        new test();
        new test();
    }
}

解析:

我们的程序是从main开始执行的,那么main这里是test方法,所以,要执行main,就要先加载test。也就是说,在加载test类的时候,此时还没执行main。

我们的程序是从main开始执行的,那么main这里是test方法,所以,要执行main,就要先加载test。也就是说,在加载test类的时候,此时还没执行main。

test继承自B,要加载test,那么就要先加载B;

B继承自A,要加载B,那么就要先加载A;

总之,只要这个类被用到了,就要先加载这个类(实例化、调用方法、调用静态方法,被继承...都算被用到)。

然后,要想要构造test,就得先构造B,要想构造B,就得先构造A,那么对于A来说,构造过程 = 构造代码块的执行 + 构造方法的执行。

还有,静态的只执行一次,所以输出结果是:


三、双亲委派模型

双亲委派模型是类加载中的一个环节,这环节处于Loading阶段。

双亲委派模型,描述的就是JVM中的类加载器,如何根据类的全限定名( java.lang.String )找到.class文件的过程。

JVM里提供了专门的对象,叫做类加载器,负责进行类加载.当然找文件的过程也是类加载器来负责的。


.class 文件,可能放置的位置有很多.有的要放到JDK目录里,有的放到项目目录里,还有的在其他特定位置,因此JVM里面提供了多个类加载器,每个类加载器负责一个片区。


默认的类加载器主要是三个:

1.BootStrapClassLoader负责加载标准库中的类(String, ArrayList, Random, Scanner..)

2.ExtensionClassLoader负责加载JDK扩展的类.(现在很少会用到)

3.ApplicationClassLoader 负责加载当前项目目录中的类~~

其实程序猿还可以自定义类加载器,来加载其他目录中的类。Tomcat就自定义了类加载器,用来专门加载 webapps里面的.class 。双亲委派模型,就描述了这个找目录过程,也就是上述类加载器是如何配合的。


1.加载java.lang.String

a.程序启动,先进入ApplicationClassLoader类加载器

b.ApplicationClassLoader就会检查下,它的父加载器是否已经加载过了.如果没有,就调用父类加载器ExtensionClassLoader

c. ExtensionClassLoader也会检查下,它的父加载器是否加载过了.如果没有,就调用父类加载器BootStrapClassLoader

d.BootStrapClassLoader也会检查下,它的父加载器是否加载过,自己没有父亲~~于是自己扫描自己负责的目录

e. java.lang.String这个类在标准库中能找到! 直接由BootStrapClassLoader负责后续的加载过程.查找环节就结束了

2.加载自己写的test类

a.程序启动,先进入ApplicationClassLoader类加载器

b.ApplicationClassLoader就会检查下,它的父加载器是否已经加载过了.如果没有,就调用父类加载器ExtensionClassLoader

c. ExtensionClassLoader也会检查下,它的父加载器是否加载过了.如果没有,就调用父类加载器BootStrapClassLoader

d.BootStrapClassLoader也会检查下,它的父加载器是否加载过,自己没有父亲~~于是自己扫描自己负责的目录,如果没有扫描到,就回到子加载器继续扫描。

e.ExtensionClassLoader也扫描自己的目录,如果没有扫描到,就回到子加载器继续扫描。

f.ApplicationClassLoader也扫描自己负责的目录,能找到test类,于是进行后续加载,查找目录的环节结束。

304882a17691456cbd6d24265a47e823.png


相关文章
|
3月前
|
前端开发 安全 Java
聊聊Java虚拟机(一)—— 类加载子系统
虚拟机就是一款用来执行虚拟计算机指令的计算机软件。它相当于一台虚拟计算机。大体上,虚拟机分为系统虚拟机和程序虚拟机。系统虚拟机就相当于一台物理电脑,里面可以安装操作系统;程序虚拟机是为了执行单个计算机程序而设计出来的虚拟机。其中 Java 虚拟机就是**执行 Java 字节码指令的虚拟机**。
45 2
|
7月前
|
前端开发 安全 Java
JVM类加载和双亲委派机制
JVM类加载和双亲委派机制
106 0
|
11天前
|
存储 前端开发 安全
JVM内部世界(内存划分,类加载,垃圾回收)(上)
JVM内部世界(内存划分,类加载,垃圾回收)
44 0
|
1月前
|
Java
一篇博客带你了解JVM中的双亲委派模型
一篇博客带你了解JVM中的双亲委派模型
|
2月前
|
安全 Java 程序员
深入理解jvm - 类加载过程
深入理解jvm - 类加载过程
50 0
|
4月前
|
存储 前端开发 安全
浅谈 JVM 类加载过程
浅谈 JVM 类加载过程
42 0
|
5月前
|
存储 算法 安全
面试~jvm(JVM内存结构、类加载、双亲委派机制、对象分配,了解垃圾回收)
面试~jvm(JVM内存结构、类加载、双亲委派机制、对象分配,了解垃圾回收)
53 0
|
7月前
|
安全 前端开发 Java
JVM概述和类加载子系统
我记得当年学java的时候,就很好奇,为什么我在IDEA上写一些代码(其实就是一堆我们人能知道的英文单词的组合加一些运算符),为什么就可以在windows上运行后执行我们的指令,而且还可以打成jar包去linux系统跑起来,为什么一份代码可以在不同平台运行呢?类是如何加载的?对象如何创建的以及都有哪些信息?我创建的对象被分配到哪个内存去了?java是怎么和我们操作系统打交道的又是怎么调用CPU为我们计算的?创建了对象分配了内存,为什么可以不用手动回收就可以自动清理内存等等等,相信你也同样有过这些困惑。
57 0
|
8月前
|
存储 安全 前端开发
【jvm系列-02】jvm的类加载子系统以及jclasslib的基本使用
【jvm系列-02】jvm的类加载子系统以及jclasslib的基本使用
52 0
|
9月前
|
存储 Java Linux
Java类加载过程、为什么会出现JVM?
也就是说Java程序可以在windows操作系统上运行,不做任何修改,同样的java程序可以在Linux操作系统上运行,跨平台。