JVM学习笔记(2)之类加载器双亲委托机制实例
JVM学习笔记(2)之类加载器双亲委托机制实例
1、什么是类加载器的双亲委托机制
类的加载器分为三种,也可以自定义,分别为Bootstrap Class Loader(启动加载器)、Extensions Class Loader(扩展加载器)、Application Class Loader(系统(App)加载器)
-
启动加载器:
它用来加载Java的核心库(JAVA_HOME/jre/lib/rt.jar 或 sun.boot.class.path 路径下的内容),是用原生代码来实现的(C++),并不继承自java.lang.ClassLoader
-
扩展加载器:
用来加载Java的扩展库(JAVA_HOME/jre/lib/ext/*.jar或 java.ext.dirs 路径下的内容) .java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类
-
系统加载器:
它根据 Java 应用的类路径(ClassPath, java.class.path)来加载。
一般来说,Java应用的类都是由它来完成加载的
双亲委托机制:
所谓双亲委托机制指的是当Class文件被加载的时候往往是先交给上级加载器加载,如果上级加载器仍然存在上级加载器则继续往上传递,当上级加载器无法加载Class文件的时候才依次往下传递,简单点说就是先往上传递,再根据当前加载器能否加载判断是否往下传递,如果传递到最下面仍然无法加载,则虚拟机加载失败,程序退出,它们的关系如下图
(图片来源于网络)
自定义类加载器:
package com.zh.classloader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; /** * @ClassName:MyClassLoader * @Description:TODO * @author:Jerry * @Date 2019/10/21 12:55 * @Version 1.0.2 **/ public class MyClassLoader0 extends ClassLoader { /** * 自定义加载器的名称 */ private String classLoaderName; //文件扩展名 private static final String FILE_EXTENSTION = ".class"; public MyClassLoader0(String classLoaderName) { //这里的super加与不加无影响? super(); this.classLoaderName = classLoaderName; } public MyClassLoader0(String classLoaderName, ClassLoader parent) { super(parent); this.classLoaderName = classLoaderName; } @Override public String toString() { return "MyClassLoader{" + "classLoaderName='" + classLoaderName + '\'' + '}'; } @Override protected Class<?> findClass(String className) throws ClassNotFoundException { System.out.println("findClass"); byte[] bytes = loadClassData(className); return defineClass(className, bytes, 0, bytes.length); } private byte[] loadClassData(String className) { System.out.println("执行了 loadClassData"); className = className.replace(".", File.separator); byte[] result = null; try (InputStream is = new FileInputStream(new File(className + FILE_EXTENSTION)); ByteArrayOutputStream bos = new ByteArrayOutputStream()) { byte[] buffer = new byte[256]; int len = 0; while ((len = is.read(buffer, 0, buffer.length)) != -1) { bos.write(buffer, 0, len); } result = bos.toByteArray(); } catch (Exception e) { e.printStackTrace(); } return result; } public static void testClassLoader(ClassLoader classLoader) throws ClassNotFoundException, IllegalAccessException, InstantiationException { Class<?> clazz = classLoader.loadClass("com.zh.classloader.Demo11"); Demo11 o = (Demo11) clazz.newInstance(); System.out.println("加载的实例对象:" + o); System.out.println("classloader:" + clazz.getClassLoader()); } public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException { MyClassLoader loader1 = new MyClassLoader("loader1"); testClassLoader(loader1); } }
打印结果并没有我们想要的:执行了 loadClassData,证明并没有执行我们覆写的findClass()方法,如图.
其实原因很简单:自定的类加载器会寻找它的双亲加载器即系统加载器去加载,系统加载器再去委托上级,只不过它的上级都无法加载,往下重新传到系统加载器,系统加载器成功加载了Demo1.class文件,所以打印的就不是我们自定义的加载器
对代码进行改动:
package com.zh.classloader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; /** * @ClassName:MyClassLoader * @Description:TODO * @author:Jerry * @Date 2019/10/21 12:55 * @Version 1.0.2 **/ public class MyClassLoader extends ClassLoader { /** * 自定义加载器的名称 */ private String classLoaderName; /** * 字节码文件路径 */ private String path; //文件扩展名 private static final String FILE_EXTENSTION = ".class"; public MyClassLoader(String classLoaderName) { //这里的super加与不加无影响? super(); this.classLoaderName = classLoaderName; } public MyClassLoader(String classLoaderName, ClassLoader parent) { super(parent); this.classLoaderName = classLoaderName; } public void setPath(String path) { this.path = path; } @Override public String toString() { return "MyClassLoader{" + "classLoaderName='" + classLoaderName + '\'' + '}'; } @Override protected Class<?> findClass(S 3ff7 tring className) throws ClassNotFoundException { System.out.println("findClass"); byte[] bytes = loadClassData(className); return defineClass(className, bytes, 0, bytes.length); } private byte[] loadClassData(String className) { System.out.println("loadClassData"); className = className.replace(".", File.separator); byte[] result = null; try (InputStream is = new FileInputStream(new File(path + className + FILE_EXTENSTION)); ByteArrayOutputStream bos = new ByteArrayOutputStream()) { // classLoaderName = classLoaderName.replace("\\", "."); byte[] buffer = new byte[256]; int len = 0; while ((len = is.read(buffer, 0, buffer.length)) != -1) { bos.write(buffer, 0, len); } result = bos.toByteArray(); } catch (Exception e) { e.printStackTrace(); } return result; } // public static void testClassLoader(ClassLoader classLoader) throws ClassNotFoundException, IllegalAccessException, InstantiationException { // Class<?> clazz = classLoader.loadClass("com.zh.classloader.Demo1"); // Object o = clazz.newInstance(); // System.out.println(o); // } public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException { MyClassLoader loader1 = new MyClassLoader("loader1"); // loader1.setPath("D:\\jvm_study\\target\\classes\\"); loader1.setPath("C:\\Users\\Jack\\Desktop\\"); Class<?> clazz = loader1.loadClass("com.zh.classloader.Demo11"); System.out.println("classloader:"+clazz.getClassLoader()); System.out.println("class:" + clazz.hashCode()); Object o = clazz.newInstance(); System.out.println(o); } }
说明:只是加了个路径,为的是改变class文件的地址,我们知道系统加载器加载的是classpath目录以及我们编写的类的目录,所以认为改动它,我们将class文件拷贝到桌面上的指定目录下,然后加载器去加载,结果如下.
这跟我们想象的结果不一样,好奇怪?好的,我们来分析下原因,我们分析下findClass是什么时候执行的,是在loadClass的时候,如下图
所以其实并未执行到findClass,如果想要让加载器加载到我们放到桌面上的class文件,可以把我们项目目录中生成的class文件删除掉,此时,系统加载器在目录下并不能找到class文件,加载失败,交给我们自定义的加载器,然后在执行findClass的时候,修改了class文件的目录,我们自定义的classloader去桌面的目录下查找,查找到了,应该能执行成功了吧,现在我删除掉了目录下的class文件,如图
执行,结果如下:
- JVM学习六:JVM之类加载器之双亲委派机制
- Java补完之类加载机制(双亲委派模型)学习笔记
- jvm原理(6)类加载器双亲委托机制详解
- JVM学习笔记(3.X)之类加载器命名空间补充
- JVM学习笔记:实例探究垃圾收集机制
- JVM 类加载器介绍及其双亲委托机制
- java类加载器学习2——自定义类加载器和父类委托机制带来的问题
- jvm(三)类加载器的父亲委托机制
- JVM学习二:JVM之类加载器之加载分析
- jvm内存JVM学习笔记-引用(Reference)机制
- JVM学习笔记5—类加载器(classloader)
- JVM学习笔记-引用(Reference)机制
- JVM笔记7:类加载器与上级委托机制
- JVM学习笔记:虚拟机的类加载机制
- 深入JVM系列(三)之类加载、类加载器、双亲委派机制与常见问题
- java之jvm学习笔记十二(访问控制器的栈校验机制)
- 深入JVM系列(三)之类加载、类加载器、双亲委派机制与常见问题
- java类加载器学习2——自定义类加载器和父类委托机制带来的问题/JAVA类加载的委托模型/Java的委托
- C#学习笔记——委托机制
- JAVA之学习笔记(1)-------------Java运行机制及JVM