JVM类加载器

108 阅读5分钟

JVM类加载器

写之前先介绍一下JVM的总体运行流程,通过一张图就可以很好的了解到了;之后每一篇关于JVM的文章都会先标出这张图: image20220301130514957.png 图1

从上面这张图可以看到,运行java程序最开始就是通过类加载器来加载字节码文件的,所以我们现在来详细了解这个类加载子系统。

类加载器

类加载器子系统作用

我们将上面的大图细分出来: 图片.png

图2

  • 类加载子系统负责从文件系统或者网络中加载.class文件
  • ClassLoader只负责.class文件的加载,至于它是否可以运行则由ExecutionEngine(执行引擎,后面会介绍到)决定。
  • 加载的类信息存放于一块称为方法区的内存空间。除了类信息之外,方法区中还会存放运行时常量信息

类加载过程

从图2可以看到类加载过程主要包括三个阶段:

  • 加载阶段
  • 链接阶段
  • 初始化阶段

分别介绍这三个阶段:

加载阶段

在加载阶段:

  • jvm通过一个类的全限定名获取定义此类的二进制字节流文件
  • 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
  • 在内存中生成一个代表这个类的.class对象,作为方法区中这个类的各种数据的访问入口(也就是通过这个类的class对象来访问这个类中的数据)

链接阶段

链接阶段包括三个步骤:

  • 验证Verify 确保.class文件的字节流中包含信息符合当前虚拟机的要求保证被加载类的正确性;
  • 准备Prepare 为类变量分配内存并且设置该类变量的默认初始值(为0) 这里不包含用final修饰的static,因为final在编译时就会分配内存 这里不会为实例变量分配初始化,类变量会分配在方法区中,而实例变量是会随着对象一起分配到中。

写到这里的时候迷糊了一下,忽然分不清类变量和实例变量的区别,翻阅别人的blog找了一下: 类变量是被static修饰的静态变量,而实例变量是没有被static修饰的,也叫对象变量 区别在于: 类变量和实例变量的区别在于:类变量是所有对象共有,其中一个对象将它值改变,其他对象得到的就是改变后的结果;而实例变量则属对象私有,某一个对象将其值改变,不影响其他对象; 图片.png 出自博客https://blog.csdn.net/qq_43918130/article/details/102369887

  • 解析Resolve 将常量池内的符号引用转换为直接引用的过程,主要针对类、接口、字段、类方法、接口方法、方法类型等等

初始化阶段

这个阶段是类加载过程的最后一步,是代码真正开始执行的时候,在这个阶段,开发人员可以根据自己的需求去给类变量初始化赋值。简单来说就是执行类构造器()方法的过程。

类加载器的分类

启动类加载器(Bootstrap ClassLoader)

负责将存放在<JAVA_HOME>\lib目录中,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的类库加载到虚拟机内存中。(注:仅按照文件名识别,如rt.jar,名字不符合的类库即使放在lib目录中也不会被加载)

扩展类加载器(Extension ClassLoader)

负责加载<JAVA_HOME>\lib\ext目录中的,或被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器。

应用程序类加载器(Application ClassLoader)

负责加载用户路径(ClassPath)上所指定的类库,开发者可以直接使用这个类加载器,一般情况下该类加载是程序中默认的类加载器。

获取类加载器的方式: 图片.png

双亲委派机制

java虚拟机对class文件采用的是按需加载的方式,也就是说当需要使用该类时才会将它的class文件加载到内存生成class对象。而且加载到某个类的class文件时,java虚拟机采用的是双亲委派模式:即把请求交由父类处理,它是一种任务委派模式

工作原理

图片.png 通俗来说就是:类加载器处理类加载请求时时候会不断递归的向上层委托,如果最上层引导类加载器能加载就交给它了,加载不了就开始向下委托,直到遇到那个能加载这个请求的类加载器,如果都加载不了就交给自定义类加载器了。

优势

  • 可以避免类的重复加载
  • 保护程序的安全,可以放置核心api得篡改

扩展

上面讲述的是系统提供的类加载器以及它们之间的关系,还有很多情况需要我们自定义类加载器。那该如何定义呢?有以下两种方式 1、如果我们自定义的加载器不想破坏双亲委派,继承java.lang.ClassLoader类并重写 findClass 方法。 2、如果使用我们自定义的加载器破坏双亲委派,继承java.lang.ClassLoader类并重写 loadClass 方法