类加载子系统之类加载机制与类加载器

    科技2023-10-31  132

    一、JVM的生命周期

    虚拟机的声明周期可以分为三个阶段:

    虚拟机的启动

    虚拟机的启动是通过引导类加载器(bootstrap class loader)创建一个初始类(initial class)来完成的,这个类是由虚拟机的具体实现来指定的。

    虚拟机的执行

    ​ 一个运行中的Java虚拟机有这一个清晰的任务就是执行Java程序,程序开始执行时虚拟机,当程序运行结束时,虚拟机就结束,在执行一个Java程序的时候,真真正正执行的是一个叫做Java虚拟机的进程。

    虚拟机的退出

    虚拟机的退出又分为几种情况:

    程序正常执行结束程序运行过程中遇到异常而终止操作系统出现错误而导致Java虚拟机进程终止某个线程调用Runtime类或System类的exit方法,或者Runtime类的halt方法,并且Java安全管理器也允许这次exit或者halt操作。还有一种就是JNI(Java Native Interface)规范描述了用JNI Invocation API来加载或卸载Java虚拟机时,Java虚拟机退出的情况。

    二、常见的Java虚拟机

    Classic VM:,它是世界上第一款商用虚拟机。这款虚拟机只提供解释器并没有即时编译器。Exact VM:sun提供的又一款虚拟机Exact Memory Management,准确式内存管理。HotSpot VM:大多数的jdk的默认虚拟机,也是Oracle JDK和Open JDK的默认虚拟机,其拥有解释器和即时编译器,在1.8的jdk中HotSpot中整合了JRockit虚拟机的优秀特性。JRockit:BEA的JRockit虚拟机,号称世界上最快的虚拟机,2008年BEA被Oracle收购。J9:IBM公司的J9虚拟机,是目前最右影响力的三大商用服务器之一。2017年,IBM发布了开源J9VM命名为OpenJ9,交给了Ecilpse基金会,也称为Ecilpase OpenJ9。TaobaoJVM:由AliJVM团队发布,是阿里巴巴基于OpenJDK开的自己的定制版本AlibabaJDk,它是国内第一个优化,深度定制且开源的高性能服务器。目前已经在淘宝天猫上线

    三、虚拟机的类加载机制

    3.1、概述

    ​ Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这个过程被称为虚拟机的类加载机制,在Java语言里面,类型的加载、连接和初始化过程都是在程序运行期间完成的。

    3.2、类加载器的作用和角色

    3.2.1 类加载器的作用

    类加载器的作用就是将类class文件加载到内存中,对方法区中的类信息的访问都需要经过这个Class对象

    3.2.1类加载器所扮演的角色

    3.3、类加载过程

    类的加载过程分为五个阶段

    3.3.1、加载过程

    类加载后会在堆区生成一个唯一的Class对象。

    3.3.2、链接过程

    在准备阶段如果变量是final static修饰的常量则不会赋予零值,而是会直接赋值如:以下代码会被直接赋值为123;

    public static int value = 123

    3.3.3、初始化过程

    类构造器是收集静态代码和静态变量形成的,如果没有静态变量和静态代码块则并不会有类构造器,初始化过程就是执行类构造器的过程。

    注意:执行接口的类加载器并不需要先执行父接口的类构造器,因为只有当父接口中定义的变量被使用时,父接口才会被初始化,此外,接口的实现类在初始化时也一样不会执行接口的类构造器

    3.4、类加载的时机

    类的加载过程只有五个阶段但是类的生命周期会经历,加载,验证,准备,解析,初始化,使用和卸载七个阶段,其中验证,准备,解析统称为链接。

    对类的主动使用会触发类的初始化

    java程序对类的主动引用:

    添加一点:深入理解Java虚拟机第三版中提到当一个接口中定义了jdk8新加入的默认方法(被default关键字修饰的接口方法)时,如果这个接口的实现类发生了初始化,那改接口要在其之前被初始化


    对类的别动引用不会触发类的初始化

    java程序对类的被动引用:

    通过子类引用父类的静态字段不会导致子类的初始化通过数组定义来引用类,不会触发此类的初始化常量在编译器会存入调用类的常量池中,本质上没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化

    四、类加载器的分类

    启动类加载器

    扩展类加载器

    在jdk9中,扩展类加载器被平台类加载器所取代

    应用程序类加载器


    用户自定义的加载器:

    为什么要自定义类加载器:

    如何自定义类加载器:

    自定义的类加载器需要继承ClassLoader,有关ClassLoader

    如何获取类加载器:

    通过方式一获取类加载器,如果获取到的结果为null,则表示是一个引导类加载器

    例如:

    // shareData是自定义类 ,String类是由引导类加载器加载的 public static void getClassLoader(){ String s = new String(); System.out.println("类加载器"+s.getClass().getClassLoader()); ShareData shareData = new ShareData(); System.out.println("类加载器"+shareData.getClass().getClassLoader()); }

    注意:判断两个class对象是否是同一个类要注意两点:

    类的全限定类型是否相等加载类的类加载器是否相同

    五、双亲委派模型

    沙箱安全机制

    双亲委派机制可以保护核心类不被篡改:

    自定义一个java.lang.String类,在main方法中会报错

    package java.lang; /** * @author mypc * @create 2020 上午 11:40 */ public class String { // static{ System.out.println("我是自定义的String类的静态代码块"); } public static void main(String[] args) { System.out.println("hello,String"); } }

    即使这个类是自定义的,理应由应用程序类加载器加载,但是由于双亲委派机制的存在,它会首先将该类交给它的父类加载器扩展类加载器加载,扩展类加载器又会交给它的父类加载器引导类加载器加载,而java.lang.String这个类在jdk的核心类库中存在,所以它会直接加载核心类库中的java.lang.String,不会加载我们自定义的java.lang.String,这样就防止了核心API被随意串改。java的核心类java.lang.String没有main方法,所以报错. 以上内容是根据尚硅谷的视屏和深入理解Java虚拟机第三版整理的笔记,如有错误请指出

    Processed: 0.024, SQL: 9