您现在的位置是:主页 > news > 网上平面设计接单平台/百度seo文章

网上平面设计接单平台/百度seo文章

admin2025/5/18 16:01:54news

简介网上平面设计接单平台,百度seo文章,浙江高端网站建设,如何设计商务网站Java类加载机制 文章目录Java类加载机制一、图示Java程序的运行流程二、类加载的过程1. 加载2. 链接2.1 验证2.2 准备2.3 解析3. 初始化3.1 类的初始化时机三、加载类的方式四、类加载器的分类1. 启动类加载器2. 扩展类加载器3. 应用程序类加载器4. 自定义类加载器五、双亲委派…

网上平面设计接单平台,百度seo文章,浙江高端网站建设,如何设计商务网站Java类加载机制 文章目录Java类加载机制一、图示Java程序的运行流程二、类加载的过程1. 加载2. 链接2.1 验证2.2 准备2.3 解析3. 初始化3.1 类的初始化时机三、加载类的方式四、类加载器的分类1. 启动类加载器2. 扩展类加载器3. 应用程序类加载器4. 自定义类加载器五、双亲委派…

Java类加载机制

文章目录

  • Java类加载机制
    • 一、图示Java程序的运行流程
    • 二、类加载的过程
      • 1. 加载
      • 2. 链接
        • 2.1 验证
        • 2.2 准备
        • 2.3 解析
      • 3. 初始化
        • 3.1 类的初始化时机
    • 三、加载类的方式
    • 四、类加载器的分类
      • 1. 启动类加载器
      • 2. 扩展类加载器
      • 3. 应用程序类加载器
      • 4. 自定义类加载器
    • 五、双亲委派机制
    • 六、破坏双亲委派机制
    • 七、沙箱安全机制
    • 八、类的生命周期

一、图示Java程序的运行流程

图1:生成字节码文件的过程

image-20210501180822213

图2:字节码文件加载的过程

image-20210501173939244

图3:类加载器子系统的详细内容

image-20210501174225560

二、类加载的过程

1. 加载

加载阶段所要进行的工作:

  • 通过类的全限定名(包名 + 类名),获取到该类的.class文件的二进制字节流(二进制字节流加载到内存)
  • 将二进制字节流所代表的静态存储结构,转化为方法区运行时的数据结构(映射成jvm能识别的结构)
  • 内存 中生成一个代表该类的java.lang.Class对象,作为方法区中这个类的各种数据的访问入口(在内存中生成class对象)
    • 通过反射可以得到此类的详细信息

2. 链接

链接是指将加载阶段已经读入内存的类的二进制数据合并到 JVM 中,使之能够执行的过程,分为验证准备解析三个阶段

2.1 验证

确保class文件中的字节流包含的信息符合当前虚拟机的要求,保证这个被加载的class类的正确性,不会危害到虚拟机的安全

2.2 准备

为类中的静态字段分配内存,并设置默认的初始值,比如 int a = 5 初始值是0。被final修饰的static字段不会设置,因为final在编译的时候就分配了

2.3 解析

  • 解析阶段的目的,是将常量池内的符号引用转换为直接引用的过程(将常量池内的符号引用解析成为实际引用)。如果符号引用指向一个未被加载的类,或者未被加载类的字段或方法,那么解析将触发这个类的加载(但未必触发这个类的链接以及初始化)

  • 事实上,解析操作往往伴随着 JVM 在执行完初始化之后再执行

  • 符号引用就是一组符号用来描述所引用的目标

  • 直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄

3. 初始化

  • 初始化就是执行类的构造器方法 init() 的过程(为类的静态变量赋予指定值,如上述的a被赋予5)

  • 若该类具有父类,jvm会保证父类的 init() 先执行,然后再执行子类的 init()

  • 如果类中存在初始化语句,就依次执行这些初始化语句,初始化语句指的是static修饰的语句

  • 对于接口

    • 初始化类时,并不会先初始化它所实现的接口
    • 初始化接口时,并不会初始化它的父接口
    • 只有当程序首次使用接口里面的变量或者调用接口中的方法时,才会导致接口初始化
  • 调用Classloader类的loadClass方法装载一个类时,并不会初始化这个类,因为这不是对类的主动使用

3.1 类的初始化时机

Java程序对类的使用方式分成:主动使用被动使用,JVM必须在每个类或接口“首次主动使用”时才初始化它们,被动使用类不会导致类的初始化,主动使用的情况:

  • 创建类实例
  • 访问某个类或接口的静态变量
  • 调用类的静态方法
  • 反射某个类
  • 初始化某个类的子类,而父类还没有初始化,则父类会被初始化
  • JVM启动的时候运行的主类
  • 接口中定义了 default 方法,当接口的实现类初始化时,此接口也会被初始化

三、加载类的方式

  • 最常见的方式
    • 本地文件系统中加载
    • 从jar等文件中加载
  • 动态的方式
    • 从Java源文件动态编译成class
  • 其他方式
    • 网络下载
    • 数据库中加载

四、类加载器的分类

上层为下层的父类加载器:

image-20210501181348256

1. 启动类加载器

  • 这个类加载器使用C/C++语言实现,嵌套在JVM内部,java程序无法直接操作这个类加载器

  • 用来加载 Java核心类库,如:JAVA_HOME/jre/lib/rt.jarresources.jarsun.boot.class.path路径下的包,用于提供jvm运行所需的包

    • 出于安全考虑,启动类加载器只加载包名为:java、javax、sun开头的类
  • 并不是继承自java.lang.ClassLoader,它没有父类加载器

  • 它加载扩展类加载器应用程序类加载器,并成为他们的父类加载器

  • 如果一个类通过 类名.class.getClassLoader() 得到的是null,表示这个类的类加载器是启动类加载器

2. 扩展类加载器

  • 由Java语言编写,可以用Java程序操作这个类加载器

  • 派生继承自java.lang.ClassLoader,父类加载器为启动类加载器

  • 这个类加载器从java.ext.dirs目录中加载类库,或者从JDK安装目录:jre/lib/ext下加载类库

  • 如果一个类通过 类名.class.getClassLoader() 得到的是…ExtensionClassloader…,表示这个类的类加载器是扩展类加载器

3. 应用程序类加载器

  • 由Java语言编写,可以用Java程序操作这个类加载器
  • 派生继承自java.lang.ClassLoader,父类加载器为扩展类加载器
  • 它负责加载环境变量classpath或者系统属性java.class.path指定路径下的类库
  • 它是程序中默认的类加载器,Java程序中的类都是由它加载完成的
  • 如果一个类通过 类名.class.getClassLoader() 得到的是…AppClassLoader…,表示这个类的类加载器是应用程序类加载器

4. 自定义类加载器

  • 一般情况下,以上3种加载器能满足日常的开发工作,不满足时,还可以自定义类加载器

  • 自定义类加载器的父类加载器一定是应用程序类加载器,其加载顺序是所有系统类加载器的最后

  • 自定义类加载器实现步骤

    • 继承ClassLoader类,重写 findClass() 方法,此方法中定义自定义类的加载逻辑
  • 为什么需要自定义类加载器

    • 需要的类不一定存放在已经设置好的classPath下(应用程序类加载器加载的路径),对于自定义路径中的class文件的加载,需要自己的ClassLoader
    • 不一定是从文件系统中读取类,可能是从网络的输入流中读取类,这就需要做一些加密和解密操作,这就需要自己实现加载类的逻辑

注意:

  1. 不需要等到某个类首次主动使用时才加载它,JVM规范允许类加载器在预料到某个类将要被使用时就预先加载它

  2. 加载后的 Java 类会被存放于方法区中,实际运行时,虚拟机会执行方法区内的代码

  3. 类加载器之间的父子关系一般不是以继承的关系来实现的,而是通常使用组合关系来复用父加载器的代码

    image-20210504205805095
  4. 为什么要有三个类加载器?

    • 各个类加载器的分工不一样,各自负责不同的区域
    • 为了实现双亲委派模型

五、双亲委派机制

  • 在加载类的时候,采用双亲委派机制,即把请求交给父类处理的一种任务委派模式

  • 双亲委派机制的代码实现

    protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException
    {synchronized (getClassLoadingLock(name)) {//首先检查请求的类是否已经被加载过Class<?> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {if (parent != null) {c = parent.loadClass(name, false); //parent为之前所述的父类加载器} else {c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {//如果父类抛出此异常,说明父类加载失败}if (c == null) {//若父类加载失败,则调用子类的findClass方法加载long t1 = System.nanoTime();c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}
    }
    
  • 工作原理

    • 如果一个类加载器收到了类加载的请求,先检查请求加载的类是否已经加载过
    • 若没有加载过,它自己不会先去加载,会把这个请求委托给父类加载器去执行,调用父加载器的 loadClass() 方法进行加载
    • 如果父类还存在父类加载器,则继续向上委托,一直委托到启动类加载器
      • 若父类加载器为空则默认使用启动类加载器作为父加载器
    • 如果父类加载器可以完成加载任务,就返回成功结果
    • 如果父类加载失败 (抛出 ClassNotFoundException 异常),就由子类调用 findClass() 自己去尝试加载,如果子类加载失败就会抛出ClassNotFoundException异常,这就是双亲委派模式
    image-20210501204443011

    这种机制的作用是保证 JDK 核心类的优先加载,越基础的类越由上层的加载器进行加载

  • 使用双亲委派模型来组织类加载器之间的关系,一个显而易见的好处就是 Java 中的类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类 java.lang.Object,它存放在rt.jar之中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都能够保证是同一个类。反之,如果没有使用双亲委派模型,都由各个类加载器自行去加载的话,如果用户自己也编写了一个名为java.lang.Object的类,并放在程序的ClassPath中,那系统中就会出现多个不同的Object类,Java类型体系中最基础的行为也就无从保证,应用程序将会变得一片混乱

  • 双亲委派机制的缺点是上层的类加载器无法访问下层的类加载器所加载的类

六、破坏双亲委派机制

  • 双亲委派模型并不是一个具有强制性约束的模型,也就是说此种模型可以被破坏

  • 如果由上层类加载器加载的基础类型想要调用用户的代码,但是启动类加载器不可能认识这些用户代码,这时就要破坏双亲委派机制

    • 在 Java 平台中,通常把核心类 rt.jar (由启动类加载器加载)中提供外部服务、可由用户层自行实现的接口称为SPI,实现SPI的代码就是上述的用户代码

    • 比如 JDBC 就涉及到了SPI的加载:

      Connection conn = DriverManager.getConnection("jdbc...", "root", "1234");
      //在以上代码执行之前,DriverManager会先被类加载器加载,因为java.sql.DriverManager类是位于rt.jar下面的 ,所以他会被根加载器加载//类加载时,会执行该类的静态方法,其中有一段关键的代码是:
      ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
      //这段代码会尝试加载classpath下面的所有实现了Driver接口的实现类//DriverManager是被根加载器加载的,那么在加载时遇到以上代码,会尝试加载所有Driver的实现类,但是这些实现类基本都是第三方提供的,根据双亲委派原则,第三方的类不能被根加载器加载
      
  • 为了解决上述问题,Java使用线程上下文类加载器

    • 这个类加载器可以通过java.lang.Thread类的 setContext-ClassLoader() 方法进行设置,如果创建线程时还未设置,它将会从父线程中继承一个,如果在应用程序的全局范围内都没有设置过的话,那这个类加载器默认就是应用程序类加载器(默认线程上下文类加载器就是应用类加载器)

    • 这是一种父类加载器去请求子类加载器完成类加载的行为,这种行为实际上是打通了双亲委派模型的层次结构来逆向使用类加载器,已经违背了双亲委派模型的一般性原则

    • 具体过程如下图所示:

      image-20210504173651459

七、沙箱安全机制

  • 沙箱的介绍

    • 沙箱是一个限制程序运行的环境
    • 将 Java 代码限定在 JVM 特定的运行范围中,并且严格限制代码对本地系统资源的访问
    • 目的是为了保证系统的安全,防止代码对本地系统造成危害
    image-20210504175407068
  • 当前最新的沙箱安全机制引入了的概念

    • 虚拟机会把所有代码加载到不同的系统域应用域

    • 系统域专门负责与操作系统的关键资源进行交互

    • 各个应用域通过系统域的代理对各种需要的资源进行访问

    • 虚拟机中不同的受保护域对应不一样的权限,存在于不同域中的类文件就具有了当前域的全部权限,如下图所示:

      image-20210504180054817

八、类的生命周期

  • 类的生命周期包括:加载、链接、初始化、使用 和 卸载

  • 加载链接初始化,属于 类加载的过程,之前已讲述

  • 使用分为主动使用与被动使用,之前已讲述

  • 卸载指对象被垃圾回收

    • 当代表一个类的Class对象不再被引用,那么Class对象的生命周期就结束了,对应的在方法区中的数据也会被卸载
    • JVM自带的类加载器装载的类,是不会卸载的,由用户自定义的类加载器加载的类是可以卸载的
      • 类可以被卸载的条件之一是加载该类的类加载器ClassLoader已经被回收了,而JVM虚拟机自带的3种类加载器是不会被卸载的