JVM中的类加载器

简介: 把类加载阶段中的"通过一个类的全限定名来获取描述此类的二进制字节流"这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类,实现这个动作的代码模块称为类加载器。


类加载器

 把类加载阶段中的"通过一个类的全限定名来获取描述此类的二进制字节流"这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类,实现这个动作的代码模块称为类加载器。

自定义类加载器

 现在有个需求在项目中我们需要加载一个特定目录下的class文件【c:\tools\myClassLoader】,这时我们需要自己来定义特定的类加载器。

1.创建自定义类加载器

 继承ClassLoader后重写了findClass方法加载指定路径上的class,代码如下:

import java.nio.file.Files;
import java.nio.file.Paths;
/**
 * 自定义类加载器
 * @author 波波烤鸭
 * @email dengpbs@163.com
 *
 */
public class MyClassLoader extends ClassLoader {
  // 加载的路径
  private String path;
  public MyClassLoader(String path) {
    super();
    this.path = path;
  }
  @Override
  protected Class<?> findClass(String name) throws ClassNotFoundException {
    try {
      byte[] result = getClass(name);
      if (result == null) {
        throw new ClassNotFoundException();
      } else {
        // 将字节流转换为Class对象
        return defineClass(name, result, 0, result.length);
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    return null;
  }
  // 加载class为字节数组
  private byte[] getClass(String name) {
    try {
      return Files.readAllBytes(Paths.get(path));
    } catch (Exception e) {
      e.printStackTrace();
    }
    return null;
  }
}

2.测试

指定目录下存放编译好的class文件,注意用相关的jdk版本编译

image.png

public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
  MyClassLoader myLoader = new MyClassLoader("C:\\tools\\myClassLoader\\User.class");
  Class clazz = myLoader.loadClass("com.dpb.pojo.User");
  Object object = clazz.newInstance();
  System.out.println(object);
  System.out.println(object.getClass().getClassLoader());
}

输出结果

com.dpb.pojo.User@4e25154f
com.dpb.loader.MyClassLoader@6d06d69c

实现了加载特定目录下的class文件

ClassLoader

 上面的代码虽然实现加载特定目录下的class文件,但这么执行的原因是什么呢?要了解这个我们需要来具体看下ClassLoader的源代码。代码比较多,截取了核心的代码

protected Class<?> loadClass(String name, boolean resolve)
     throws ClassNotFoundException
 {
     synchronized (getClassLoadingLock(name)) {
         // First, check if the class has already been loaded
         Class<?> c = findLoadedClass(name);
         // 获取类加载器 如果为null 本方法就结束了
         if (c == null) {
             long t0 = System.nanoTime();
             try {
               // 如果parent为null
                 if (parent != null) {
                  // 获取 父类加载器
                     c = parent.loadClass(name, false);
                 } else {
                  // 使用引导加载器
                     c = findBootstrapClassOrNull(name);
                 }
             } catch (ClassNotFoundException e) {
                 // ClassNotFoundException thrown if class not found
                 // from the non-null parent class loader
             }
       // 如果所有的父加载器都没有找到Class
             if (c == null) {
                 // If still not found, then invoke findClass in order
                 // to find the class.
                 long t1 = System.nanoTime();
                 // 就调用自身的findClass方法去加载类
                 c = findClass(name);
                 // this is the defining class loader; record the stats
                 sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                 sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                 sun.misc.PerfCounter.getFindClasses().increment();
             }
         }
         if (resolve) {
             resolveClass(c);
         }
         return c;
     }
 }
// 本方法并没有去查找Class,本方法留给子类去重写的
protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
}

 所以如果我们需要加载特定的Class文件的时候只需要重写findClass方法即可,而不用去重写loadClass方法。

双亲委派模型

 通过ClassLoader中的loadClass方法我们发现类加载器加类的时候有既定的原则,而且系统提供的类加载器好像也不止一个,我们就来说下这块。系统给我们提供了三个类加载器,如下

序号 类加载 说明

1 启动类加载器

Bootstrap ClassLoader 加载<JAVA_HOME> \lib目录下或-Xbootclasspath指定路径下能被虚拟机识别的类库加载到虚拟机中(rj.jar) ,无法被java程序直接是使用

2 扩展类加载器

Extension ClassLoader 负责加载<JAVA_HOME> \lib\ext目录中或者被java.ext.dirs指定的目录下的类库,程序员可以直接使用该加载器

3 应用程序类加载器

Application ClassLoader 也称系统类加载器,负责加载用户类路径上所指定的类库,一般是程序默认的类加载器

image.png

1.启动类加载器

public static void main(String[] args) {
  System.out.println("BootStrap:"+String.class.getClassLoader());
  System.out.println(System.getProperty("sun.boot.class.path"));
}

启动类加载器我们无法通过程序获取,所以打印结果为null,可是加载资源的路径可以获取。

BootStrap:null
C:\Program Files\Java\jre1.8.0_144\lib\resources.jar;
C:\Program Files\Java\jre1.8.0_144\lib\rt.jar;
C:\Program Files\Java\jre1.8.0_144\lib\sunrsasign.jar;
C:\Program Files\Java\jre1.8.0_144\lib\jsse.jar;
C:\Program Files\Java\jre1.8.0_144\lib\jce.jar;
C:\Program Files\Java\jre1.8.0_144\lib\charsets.jar;
C:\Program Files\Java\jre1.8.0_144\lib\jfr.jar;
C:\Program Files\Java\jre1.8.0_144\classes

2.扩展类加载器

public static void main(String[] args) {
  System.out.println(System.getProperty("java.ext.dirs"));
}

加载路径如下:

C:\Program Files\Java\jre1.8.0_144\lib\ext;
C:\Windows\Sun\Java\lib\ext

我们也可以将自己的文件打成jar包放到扩展目录下,也会被扩展类加载器加载。

3.系统类加载器

public static void main(String[] args) {
  System.out.println(System.getProperty("java.class.path"));
}

加载路径

C:\Users\dengp\Desktop\共享文件\Java1112\workspace\others\FreemarkerDemo\target\classes;
C:\Users\dengp\.m2\repository\org\springframework\spring-context\4.3.21.RELEASE\spring-context-4.3.21.RELEASE.jar;
C:\Users\dengp\.m2\repository\org\springframework\spring-aop\4.3.21.RELEASE\spring-aop-4.3.21.RELEASE.jar;
C:\Users\dengp\.m2\repository\org\springframework\spring-beans\4.3.21.RELEASE\spring-beans-4.3.21.RELEASE.jar;
C:\Users\dengp\.m2\repository\org\springframework\spring-core\4.3.21.RELEASE\spring-core-4.3.21.RELEASE.jar;
C:\Users\dengp\.m2\repository\commons-logging\commons-logging\1.2\commons-logging-1.2.jar;
C:\Users\dengp\.m2\repository\org\springframework\spring-expression\4.3.21.RELEASE\spring-expression-4.3.21.RELEASE.jar;
C:\Users\dengp\.m2\repository\org\springframework\spring-webmvc\4.3.21.RELEASE\spring-webmvc-4.3.21.RELEASE.jar;
C:\Users\dengp\.m2\repository\org\springframework\spring-web\4.3.21.RELEASE\spring-web-4.3.21.RELEASE.jar;
C:\Users\dengp\.m2\repository\org\freemarker\freemarker\2.3.28\freemarker-2.3.28.jar;
C:\Users\dengp\.m2\repository\org\springframework\spring-context-support\4.3.21.RELEASE\spring-context-support-4.3.21.RELEASE.jar

双亲委派描述:

   如果一个类加载器收到了类加载请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成,每一个层次的类加载器都是如果,因此所有的加载请求最终都应该传递到顶层的启动类加载器中

   当父加载器反馈无法加载该类时(搜索范围中没有找到所需的类),子加载器才会尝试自己去加载。

弄清楚这个委派模型后再去看loadClass方法中的逻辑应该就比较容易了。

参考《深入理解Java虚拟机》


相关文章
|
4月前
|
Java
jvm---类加载器(1)
jvm---类加载器(1)
|
17天前
|
安全 前端开发 Java
【JVM的秘密揭秘】深入理解类加载器与双亲委派机制的奥秘!
【8月更文挑战第25天】在Java技术栈中,深入理解JVM类加载机制及其双亲委派模型是至关重要的。JVM类加载器作为运行时系统的关键组件,负责将字节码文件加载至内存并转换为可执行的数据结构。其采用层级结构,包括引导、扩展、应用及用户自定义类加载器,通过双亲委派机制协同工作,确保Java核心库的安全性与稳定性。本文通过解析类加载器的分类、双亲委派机制原理及示例代码,帮助读者全面掌握这一核心概念,为开发更安全高效的Java应用程序奠定基础。
51 0
|
4月前
|
前端开发 安全 Java
深入浅出JVM(八)之类加载器
深入浅出JVM(八)之类加载器
|
11天前
|
数据库 C# 开发者
WPF开发者必读:揭秘ADO.NET与Entity Framework数据库交互秘籍,轻松实现企业级应用!
【8月更文挑战第31天】在现代软件开发中,WPF 与数据库的交互对于构建企业级应用至关重要。本文介绍了如何利用 ADO.NET 和 Entity Framework 在 WPF 应用中访问和操作数据库。ADO.NET 是 .NET Framework 中用于访问各类数据库(如 SQL Server、MySQL 等)的类库;Entity Framework 则是一种 ORM 框架,支持面向对象的数据操作。文章通过示例展示了如何在 WPF 应用中集成这两种技术,提高开发效率。
25 0
|
11天前
|
开发者 C# Windows
WPF布局大揭秘:掌握布局技巧,轻松创建响应式用户界面,让你的应用程序更上一层楼!
【8月更文挑战第31天】在现代软件开发中,响应式用户界面至关重要。WPF(Windows Presentation Foundation)作为.NET框架的一部分,提供了丰富的布局控件和机制,便于创建可自动调整的UI。本文介绍WPF布局的基础概念与实现方法,包括`StackPanel`、`DockPanel`、`Grid`等控件的使用,并通过示例代码展示如何构建响应式布局。了解这些技巧有助于开发者优化用户体验,适应不同设备和屏幕尺寸。
13 0
|
17天前
|
安全 前端开发 Java
【JVM 探秘】ClassLoader 类加载器:揭秘 Java 类加载机制背后的秘密武器!
【8月更文挑战第25天】本文全面介绍了Java虚拟机(JVM)中的类加载器,它是JVM的核心组件之一,负责将Java类加载到运行环境中。文章首先概述了类加载器的基本工作原理及其遵循的双亲委派模型,确保了核心类库的安全与稳定。接着详细阐述了启动、扩展和应用三种主要类加载器的层次结构。并通过一个自定义类加载器的例子展示了如何从特定目录加载类。此外,还介绍了类加载器的完整生命周期,包括加载、链接和初始化三个阶段。最后强调了类加载器在版本隔离、安全性和灵活性方面的重要作用。深入理解类加载器对于掌握JVM内部机制至关重要。
34 0
|
2月前
|
存储 前端开发 Java
(二)JVM成神路之剖析Java类加载子系统、双亲委派机制及线程上下文类加载器
上篇《初识Java虚拟机》文章中曾提及到:我们所编写的Java代码经过编译之后,会生成对应的class字节码文件,而在程序启动时会通过类加载子系统将这些字节码文件先装载进内存,然后再交由执行引擎执行。本文中则会对Java虚拟机的类加载机制以及执行引擎进行全面分析。
|
3月前
|
安全 前端开发 Java
《JVM由浅入深学习【一】 》JVM由简入深学习提升(类加载过程+父子类加载过程+类加载器+双亲委派机制)
《JVM由浅入深学习【一】 》JVM由简入深学习提升(类加载过程+父子类加载过程+类加载器+双亲委派机制)
30 0
|
4月前
|
监控 安全 Java
JVM工作原理与实战(九):类加载器-启动类加载器
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了启动类加载器、通过启动类加载器去加载用户jar包等内容。
64 8
|
4月前
|
监控 安全 Java
JVM工作原理与实战(十):类加载器-Java类加载器
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了扩展类加载器、通过扩展类加载器去加载用户jar包、应用程序类加载器等内容。
83 4