JVM之类加载

本文深入探讨Java中的类加载机制,包括加载、连接、初始化等阶段,以及类加载器的工作原理。同时介绍了双亲委派模型,确保类的唯一性和安全性。文中还提到了类加载器的分类,如引导类加载器、扩展类加载器和应用程序类加载器,并讨论了自定义类加载器的场景。最后,文章分析了双亲委派机制的优势,如避免类重复加载和保护核心API不受篡改。
摘要由CSDN通过智能技术生成

在java中,类加载和它的双亲委派机制是很重要的一部分知识,本文中通过四个方面,重点说一下这个过程。

一、类加载机制

在了解类加载过程之前,我们先了解一下java文件的编译过程,就是通过javac编译生成.class文件,在通过JVM生成机器码的过程。

它的主要过程有:

1、词法分析

2、语法分析

3、中间代码生成

4、代码优化

5、代码生成

经过这五个过程,就会将.java文件变成.class文件,这个.class文件就是JVM可以识别的代码文件。

这个.class文件是一个字节码文件,它就可以被进行类加载了,在加载的时候,会先将class文件加载进JVM中,这时候它叫做DNA元数据模板,放在方法区中,而在class文件——>JVM——>元数据模板的过程中需要一个运输工具扮演相当于快递员的角色,这就是类加载器——ClassLoder

类加载器(ClassLoder)在加载.class文件的时候有三个子阶段,分别是:


1、加载阶段

加载阶段主要干这几件事:

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

2、将这个字节流定义的静态存储结构转化为方法区的运行时数据结构。

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

这个过程中的任务比较简单,我们主要知道会生成一个.Class实例就好。

2、连接阶段

连接阶段其实还分为三个子阶段。

2.1、验证(Verify)

这一步主要保证字节码文件的包含信息符合当前虚拟机要求,并且不会有什么东西危害虚拟机,保证被加载类地正确性。

主要包括四种验证:

1、文件格式验证

2、元数据验证

3、字节码验证

4、符号引用验证

2.2、准备(Prepaer)

这个阶段会为类变量(静态变量)进行分配内存并且默认初始化,也就是赋零值。

这里注意几点:

这里如果静态变量被final关键字修饰,这里是不会赋值的,因为这已经是一个常量了,常量在编译阶段就会分配,准备阶段会显式初始化

这里也不会为实例变量初始化,类变量在方法区中,实例变量会虽对象一起分配到堆(Heap)中。

2.3、解析(Resolve)

将常量池内的符号引用替换为直接引用的过程。

两个重点:

符号引用。即一个字符串,但是这个字符串给出了一些能够唯一性识别一个方法,一个变量,一个类的相关信息。

直接引用。可以理解为一个内存地址,或者一个偏移量。比如类方法,类变量的直接引用是指向方法区的指针;而实例方法,实例变量的直接引用则是从实例的头指针开始算起到这个实例变量位置的偏移量

举个例子来说,现在调用方法hello(),这个方法的地址是1234567,那么hello就是符号引用,1234567就是直接引用。

3、初始化阶段

初始化阶段就是执行类的构造器方法<clinit>()的过程。

这个方法不需要定义,是javac编译器收集所有类变量的赋值动作静态代码块中的语句生成的。

构造器方法中的语句按照在源文件中的顺序执行。

这里注意:<clinit>()方法不同于类的构造器(构造方法)

如果这个类有父类,JVM就会保证子类的<clinit>()执行前,父类的<clinit>()执行完成,并且保证一个类的<clinit>()在多线程的时候被同步加锁。

到这里,类就被加载完成了。

二、类加载器的分类

JVM实际上支持两种类加载器,分别是引导类加载器(Bootst ClassLoader)自定义类加载器(User-Defined-ClassLoader)。

从概念上来讲,自定义类加载器应该是用户自己定义出来使用的加载器,但是JVM没有这么规定,而是将所有派生于抽象类接口ClassLoader的加载器,都归结于自定义类加载器。

但是无论怎么划分,我们程序中最常用的类加载器只有三种,如图所示:

接下来我们逐一说明:

1、启动类加载器/引导类加载器Bootstrap ClassLoader(JVM自带)

这个加载器用C/C++语言实现,嵌套在JVM语言内部。

它的用途有:

1、加载Java的核心库(JAVA_Home、jre、lib、class.path下的内容),提供JVM自身需要的类。

2、不继承java.long.ClassLoader,没有父类。

3、加载扩展类和应用程序类加载器,指定为它们的类加载器。

3、出于安全考虑,它只加载包命java、javax,sun等开头的类。

2、扩展类加载器 ExtensionClassLoader

这个加载器使用java语言书写的,派生于ClassLoader接口。

父类加载器是启动类加载器。

从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK安装目录jer/lib/ext子目录下加载类库。

如果是用户自定义的jar放在此目录下也会被该加载器加载。

3、系统类加载器/应用程序类加载器AppClassLoader

和ExtensionClassLoader一样,AppClassLoader也是使用java语言书写的,派生于ClassLoader接口。

但是它的父类加载器是扩展类加载器。

他负责加载环境变量classpath或者java.class.path指定路径下的类库。

该加载器是程序中默认的类加载器,一般来说,java应用的类都是有它来加载的。

4、用户自定义类加载器

在java中,类加载一般都是由以上三种加载器配合使用加载的,但是必要的时候我们还是可以自定义要使用的类加载器的。

那么什么场景下需要自定义类加载器呢?

1、隔离加载类

2、修改类加载的方式

3、扩展加载源

4、防止源码泄露

那么用户该怎么自定义类加载器呢?

这个我们下一篇博客详细阐述。

三、双亲委派机制

双亲委派机制其实非常简单,它是也是一种类加载的机制,但是是一种按需加载的方法,也就是说什么时候需要这个类了,才将它的class文件加载进内存中生成Class对象,当加载这个类的时候,就用的是双亲委派机制,即把请求交给父类处理,它是一种委派模式。

1、工作原理

1、如果一个类加载器收到了一个类的加载请求,他不会自己去加载,而是将这个过程委托给父类去加载。

2、如果父类加载器还有父类加载器,那么就将这个类继续向上委托,交给父类的父类,知道交给顶层的类加载器。

3、如果父类加载器能完成这个加载,那么他就去完成加载,如果不能完成,他才会交给子类加载器去完成。

这就是双亲委派机制,如图所示:

那么这种加载方式有什么优缺点呢?

2、双亲委派机制的优点

说了这么多,那么为什么要用双亲委派机制呢?

首先,采用双亲委派模式的是好处是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。

其次是考虑到安全因素,java核心api中定义类型不会被随意替换,假设通过网络传递一个名为java.lang.Integer的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的java.lang.Integer,而直接返回已加载过的Integer.class,这样便可以防止核心API库被随意篡改。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值