其它

JVM系列之ClassLoader

2021-01-21 12:12:09 阅读数 9194 收藏 0
点击上方“蓝字”,关注我们.

前言:

了解JVM-类加载之前我们先了解下为什么要这样? 学java 的都知道Java 是跨平台语言,一套代码到处运行,那为什么他可以跨平台呢?

简单的说下:之所以可以跨平台运行 是依靠不同平台的JVM,我们编写代码, javac 编译成.class 文件,然后通过类加载器到JVM

内存之中,紧接着 Java解释器将字节码文件翻译成机器代码,执行并显示结果,知道了前因后果 ,那我们来了解下 这个类加载器是怎么玩的呢?

称述一些概念方便理解

字节码文件是一种和任何具体机器环境及操作系统环境无关的中间代码,它是一种二进制文件,是Java源文件由Java编译器编译后生成的目标代码文件。编程人员和计算机都无法直接读懂字节码文件,它必须由专用的Java解释器来解释执行,因此Java是一种在编译基础上进行解释运行的语言。

Java解释器负责将字节码文件翻译成具体硬件环境和操作系统平台下的机器代码,以便执行。因此Java程序不能直接运行在现有的操作系统平台上,它必须运行在被称为Java虚拟机的软件平台之上。

完整的过程如下,图片来源于网络

了解了大体概貌我们接着去了解细节!

1.什么是类加载?

通俗的讲就是将编译后的.class 加载到内存,经过类加载器处理之后成为可以被虚拟机直接使用的

数据, .class 文件类加载之后在jvm中形成一份描述Class 结构的元信息对象,通过该元信息可以知道

class的结构信息:如构造函数,属性和方法等,Java允许用户借由这个Class相关的元信息对象

间接调用Class对象的功能,这里就是我们经常能见到的Class类。

2. 类加载过程

类加载完毕结果如图

3. 类加载器

Java类加载器是一部分的Java运行时环境可以动态加载Java类到Java虚拟机。由于类加载器的原因,Java运行时系统不需要了解文件和文件系统。

Java类不会一次全部加载到内存中,而是在应用程序需要时加载。此时,JRE将调用Java ClassLoader,并且这些ClassLoader将类动态加载到内存中。

并非所有类都由单个ClassLoader加载。根据类的类型和类的路径,决定加载该特定类的ClassLoader。要了解加载类的ClassLoader,请使用getClassLoader()方法。所有类均基于其名称进行加载,如果未找到这些类中的任何一个,则它将返回NoClassDefFoundError或ClassNotFoundException。

1. Java Classloader具有以下三种类型

1.BootStrap类加载器: Bootstrap类加载器是一种机器代码,当JVM调用它时会启动操作。它不是一个Java类。它的工作是加载第一个纯Java ClassLoader。Bootstrap ClassLoader从rt.jar位置加载类。Bootstrap ClassLoader没有任何父ClassLoader。也称为Primodial ClassLoader。

2.扩展ClassLoader:扩展ClassLoader是Bootstrap ClassLoader的子级,并从相应的JDK扩展库中加载核心Java类的扩展。它从jre / lib / ext目录或系统属性java.ext.dirs指向的任何其他目录中加载文件。

3.系统类加载器:应用程序类加载器也称为系统类加载器。它加载在环境变量CLASSPATH,-classpath或-cp命令行选项中找到的应用程序类型类。Application ClassLoader是Extension ClassLoader的子类。

注意:ClassLoader委托层次结构模型始终按Application ClassLoader-> Extension ClassLoader-> Bootstrap ClassLoader的顺序运行。始终给Bootstrap ClassLoader更高的优先级,其次是Extension ClassLoader,然后是Application ClassLoader。

2 Java ClassLoader有功能性三个原则,它们是:

1.委托模型:Java虚拟机和Java ClassLoader使用一种称为“ 委托层次算法”的算法将类加载到Java文件中。

ClassLoader基于委托模型提供的一组操作进行工作。他们是:

ClassLoader始终遵循委托层次结构原则。

每当JVM遇到一个类时,它都会检查该类是否已加载。

如果该类已经在方法区域中加载,则JVM继续执行。

如果该类不在方法区域中,则JVM要求Java ClassLoader子系统加载该特定类,然后ClassLoader子系统将控件移交给Application ClassLoader。

然后,应用程序ClassLoader将请求委托给Extension ClassLoader,而Extension ClassLoader依次将请求委托给Bootstrap ClassLoader。

Bootstrap ClassLoader将在Bootstrap类路径(JDK / JRE / LIB)中搜索。如果该类可用,则将其加载,否则将请求委托给Extension ClassLoader。

Extension ClassLoader在扩展类路径(JDK / JRE / LIB / EXT)中搜索类。如果该类可用,则将其加载,否则将请求委托给Application ClassLoader。

Application ClassLoader在应用程序类路径中搜索该类。如果该类可用,则将其加载,否则,将生成ClassNotFoundException异常。

2.可见性原则:可见性原则指出,父ClassLoader加载的类对子ClassLoader可见,但子ClassLoader加载的类对父 ClassLoader不可见。假设扩展类加载器已加载了GEEKS.class类,则该类仅对扩展类加载器和应用程序类加载器可见, 而对引导类加载器不可见。如果再次尝试使用Bootstrap ClassLoader加载该类,它将给出异常java.lang.ClassNotFoundException。

3.Uniqueness 属性:Uniquesness属性可确保类是唯一的,并且不会重复类。这还确保了由父类加载器加载的类不会由子类加载器加载。如果父类加载器无法找到该类,则只有当前实例自己会尝试这样做。

Java.lang.ClassLoader的方法

在JVM请求该类之后,将遵循一些步骤以加载一个类。按照委托模型加载类,但是有一些重要的方法或函数在加载类中起着至关重要的作用。

loadClass(String name,boolean resolve):此方法用于加载JVM引用的类。它以类的名称为参数。类型为loadClass(String,boolean)。

defineClass():defineClass()方法是最终方法,不能被覆盖。此方法用于将字节数组定义为class的实例。如果该类无效,则抛出ClassFormatError。

findClass(String name):此方法用于查找指定的类。此方法仅查找但不加载类。

findLoadedClass(String name):此方法用于验证JVM引用的Class是否先前已加载。

Class.forName(String name,boolean initialize,ClassLoader loader):此方法用于加载类以及初始化类。此方法还提供选择任何一个ClassLoader的选项。如果ClassLoader参数为NULL,则使用Bootstrap ClassLoader。

示例:在加载类之前执行以下代码:

protected synchronized Class<?>
loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
Class c = findLoadedClass(name);
try {
if (c == NULL) {
if (parent != NULL) {
c = parent.loadClass(name, false);
}
else {
c = findBootstrapClass0(name);
}
}
catch (ClassNotFoundException e)
{
System.out.println(e);
}
}
}


注意:如果已经加载了一个类,它将返回它。否则,它将对新类的搜索委托给父类加载器。如果父类加载器找不到该类,则loadClass()调用方法findClass()来查找和加载该类。

扫码关注

酷爽一夏