JVM:解析 类装载器ClassLoader

JVM的位置

JVM就是java虚拟机,它是一个虚构出来的计算机,可在实际的计算机上模拟各种计算机的功能。
在这里插入图片描述
JVM是运行在操作系统之上的,它与硬件没有直接的交互。

作用
JVM是java字节码执行的引擎,还能优化java字节码,使之转化成效率更高的机器指令。
JVM中类的装载是由类加载器和它的子类来实现的,类加载是java运行时一个重要的系统组件,负责在运行时查找和装入类文件的类。
不同的平台对应着不同的JVM,在执行字节码(class文件)时,JVM负责将每一条要执行的字节码送给解释器,解释器再将其翻译成特定平台换将的机器指令并执行,这样就实现了跨平台运行。

JVM体系结构概览:
在这里插入图片描述
JVM主要分为四个组成部分:

  1. Class Loader (类加载器)
  2. Runtime Data Area (运行时数据区域)
  3. Execution Engine (执行引擎)
  4. Native Interface (本地方法接口)

这篇主要讲Class Loader (类加载器)

ClassLoader

定义

ClassLoader负责加载class文件,class文件在文件开头有特定的文件标识,将class文件字节码内容加载到内存中,并将这些内容转换成方法区中的运行时数据结构,并且ClassLoader只负责class文件的加载,至于它是否可以运行,则由Execution Engine决定 。

在这里插入图片描述
class文件开头的特定标识如下:
在这里插入图片描述
java程序编译生成.class字节码文件之后,由 类加载器 将字节码文件加载到内存中,并将其转换为方法区中的运行时数据结构(大写Class模板),然后存放在方法区,程序需要实例化对象时,就按照方法区中对应的“模板”来创建对象。

四种ClassLoader

ClassLoader有4种,JVM自带的有3种,另一种是自定类加载器。

虚拟机自带的加载器

  1. 启动类加载器(Bootstrap)C++
  2. 扩展类加载器(Extension)Java
  3. 应用程序类加载器(AppClassLoader)Java也叫系统类加载器,加载当前应用的classpath的所有类

用户自定义加载器
Java.lang.ClassLoader的子类,用户可以定制类的加载方式

四种类加载器有继承关系,如下:
在这里插入图片描述
启动类加载器 是所有类加载的根类,其次是 扩展类加载器,然后是 应用程序类加载器,最后才是 用户自定义类架子器。
注意,启动类加载器 是C++实现的。

不同ClassLoader分别加载哪些类
  1. 启动类加载器:加载 "JAVAHOME/jre/lib/rt.jar"下的类对应的字节码文件,rt是runtime的缩写,加载核心的类库(java.lang.*等),构造ExtClassLoader和APPClassLoader。
  2. 扩展类加载器:加载 "JAVAHOME/jre/lib/ext/*.jar"下的类,该路径下的类是后来扩展的类
  3. 应用程序类加载器:加载类路径下的类"CLASSPATH",即加载用户自定的类。
案例
public class MyObject {
    public static void main(String[] args) {

        Object obj = new Object();
        System.out.println(obj.getClass().getClassLoader());//打印加载Object的类加载器
//        System.out.println(obj.getClass().getClassLoader().getParent());
//        System.out.println(obj.getClass().getClassLoader().getParent().getParent());

        System.out.println();

        MyObject myObject = new MyObject();
        System.out.println(myObject.getClass().getClassLoader());//打印加载MyObject的类加载器
        System.out.println(myObject.getClass().getClassLoader().getParent());//打印加载MyObject的类加载器的父类
        System.out.println(myObject.getClass().getClassLoader().getParent().getParent());//打印加载MyObject的类加载器的父类的父类

    }
}

注意:由于启动类加载器 是有C++实现的,java没有这个类,因此输出为null。

  • Object是jdk自带的类,在rt.jar下,所以由Bootstrap ClassLoader加载。
  • MyObject是我们自己写的类,所以由AppClassLoader加载,AppClassLoader的父类是Extension ClassLoader,Extension ClassLoader父类是Bootstrap ClassLoader。
  • sun.misc.Launcher是JVM的入口程序。

因此打印结果如下:

在这里插入图片描述

双亲委派机制

定义:
当某个类加载器需要加载某个.class文件时,它首先把这个任务委托给他的上级类加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类。

比如:当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每一个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载其中,只有当父类加载器反馈自己无法完成这个请求的时候(在它的加载路径下没有找到所需加载的Class),子类加载器才会尝试自己去加载,如果最后的子类也没有发现该Class就会报错ClassNotFoundException。

流程图

在这里插入图片描述

作用:

  • 沙箱安全,防止恶意代码去干涉原有代码。比如,用户自定义了一个java.lang.String类,在加载该类的.class的时候,需要先看上级Bootstrap ClassLoader是否有该Class,而java.lang.String在启动类加载器中有,就不会使用APPClassLoader加载我自定义的java.lang.String;从而保证了原有代码的安全性。
  • 防止重复加载同一个.class。通过委派从下级往上级逐层检查是否加载过,加载过了,就不用再加载一遍。保证数据安全。

案例
就以自定义java.lang.String为例:

package java.lang;

public class String {
    public static void main(String[] args) {
        System.out.println("*****自定义java.lang.String*****");
    }
}

运行结果:
在这里插入图片描述
因为启动类加载器加载的是jdk自带的java.lang.String,该类中没有main 方法,因此报错。可以看出双亲委派机制保证了原因代码不被覆盖。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值