ClassLoader和双亲委派机制

来源:互联网 发布:matlab模拟退火算法 编辑:程序博客网 时间:2024/04/30 20:43

博文主要讲classloader的模型、作用和使用,内容是作者学习java反射机制有关知识时记录的笔记。

ClassLoader

ClassLoad:类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class 类的一个实例。

双亲委派机制

某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。

自己对JVM中的ClassLoader和双亲委派机制的一些理解:

  • java虚拟机中的class其实都是通过classloader来装载的
  • 只有当你使用该class的时候才会去装载,一个classloader只会装载同一个class一次。
  • 不同的类加载器的实例所加载的字节码文件,其通过反射获取的对象不是相同类型(相互赋值会抛出类型强转异常)。即:判断两个类是否为同一对象的标准里面有一条是类加载器必须为相同。
  • 双亲委派机制能在很大程度上防止内存中出现多个相同的字节码文件。
  • 在加载类的时候默认会使用当前类的ClassLoader进行加载(类A中引用了类B,JVM会用类A的类加载器加载类B)。
  • 在线程中加载一个类的时候:当前线程的类加载器可以通过Thread类的getContextClassLoader()获得,也可以通过setContextClassLoader()自己设置类加载器(PS:自己没有试验过)。

ClassLoader体系结构图

ClassLoader体系结构图

package com.tzx.reflection;public class MyClassLoader {    public static void main(String[] args) {        // TODO Auto-generated method stub        System.out.println(Thread.currentThread().getContextClassLoader());        System.out.println(ClassLoaderTest.class.getClassLoader());        System.out.println(System.class.getClassLoader());        System.out.println(ClassLoader.getSystemClassLoader());        System.out.println(ClassLoader.getSystemClassLoader().getParent());        System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent());    }}

运行结果:

sun.misc.Launcher$AppClassLoader@4aa298b7sun.misc.Launcher$AppClassLoader@4aa298b7nullsun.misc.Launcher$AppClassLoader@382f3bf0sun.misc.Launcher$ExtClassLoader@25082661null

按照双亲委派机制加载类,每当需要加载一个新的类时,当前的类加载器会先委托其父加载器,查询有没有加载该类。如果父类加载器已近加载该类,那么直接返回加载的class对象,如果没有那么继续向上寻找父类加载器,如果在祖宗类加载器Bootstrap都没有加载该类,那么需要当前的类加载器自己加载,如果当前的类加载器也不能加载则会跑出ClassNotFoundException异常 (PS:类加载器没有向下寻找,没有getChild只有getParent)。

用这种思想去解析上边代码:Thread.currentThread().getContextClassLoader()指出当前的类加载器是AppClassLoader,需要加载MyClassLoader.class先在父类加载器(ExtClassLoader)中寻找,没有再向祖宗类加载中寻找(Bootstrap ClassLoader),还没有找到那么AppClassLoader自己去加载。

JVM中的类加载器类型:

  • (Bootstrap ClassLoader)启动类加载器:
    负责加载java_home/jar/lib/rt.jar目录下的核心类或- Xbootclasspath指定目录下的类。由于引导类加载器全部是native代码来实现的并且涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作,从上面的ClassLoader体系结构图中可以看出在java代码中获取启动类加载器为null。

rt.jar.png

像System.java这样的由系统提供的类都在rt.jar中,由Bootstrap ClassLoader加载,由于Bootstrap类加载器不是Java写的,所以打印出来的类名为null。

  • (Extension)扩展类加载器:负责加载java_home/lib/ext目录下的扩展类或 -Djava.ext.dirs 指定目录下的类。 开发者可以直接使用标准扩展类加载器。

我们将第一段代码生产的MyClassLoader.class文件打包成jar(java打包成jar|执行jar包中的main方法),放在java_home/jar/lib/ext目录下。

ext.png

再次执行该java程序

im@58user:/usr/lib/jvm/jdk1.8.0_101/jre/lib/ext$ java -cp MyClassLoader.jar com.loadclass.demo.ClassLoaderTest startsun.misc.Launcher$AppClassLoader@55f96302sun.misc.Launcher$ExtClassLoader@70dea4enullsun.misc.Launcher$AppClassLoader@55f96302sun.misc.Launcher$ExtClassLoader@70dea4enull

通过终端输出结果我们可以看到执行ClassLoaderTest程序的类加载器是AppClassLoader,但加载ClassLoaderTest类的类加载器是ExtClassLoader。因为java_home/jar/lib/ext/.jar在执行程序之前就被ExtClassLoader类加载器加载过了。这样避免了类的重复加载~!~!*

  • (System)类加载器:负责加载-classpath/-Djava.class.path所指的目录下的类。开发者可以直接使用标准扩展类加载器。一般来说,Java应用的类都是由他来完成加载的。
    除了以上三种类型的类加载器,还有一个中比较特殊的:线程上下文类加载器(PS:暂时没做这方面的记录)。

自定义类加载器

  • 定义
package com.tzx.reflection;import java.io.ByteArrayOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.IOException;public class MyClassLoader extends ClassLoader{    static {        System.out.println("MyClassLoader");    }    public static final String driver = "/home/im/Desktop/";    public static final String fileTyep = ".class";    public Class findClass(String name) {        byte[] data = loadClassData(name);        return defineClass(data, 0, data.length);    }    public byte[] loadClassData(String name) {        FileInputStream fis = null;        byte[] data = null;        try {            File file = new File(driver + name + fileTyep);            System.out.println(file.getAbsolutePath());            fis = new FileInputStream(file);            ByteArrayOutputStream baos = new ByteArrayOutputStream();            int ch = 0;            while ((ch = fis.read()) != -1) {                baos.write(ch);            }            data = baos.toByteArray();        } catch (IOException e) {            e.printStackTrace();            System.out.println("loadClassData-IOException");        }        return data;    }}
  • 使用
public class ClassLoaderTest {    public static void main(String[] args) {        MyClassLoader cl1 = new MyClassLoader();        //磁盘中/home/im/Desktop/Hello.class文件存在        try {            Class c1 = cl1.loadClass("Hello");            Object object = c1.newInstance();        } catch (ClassNotFoundException e) {            e.printStackTrace();            System.out.println("main-ClassNotFoundException");        } catch (IllegalAccessException e) {            e.printStackTrace();        } catch (InstantiationException e) {            e.printStackTrace();        }    }}
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 cad证书丢了怎么办 学信网注册身份证重复怎么办 学历和学位认证怎么办 假学历无法认证怎么办 学历认证没照片怎么办? 毕业证封皮丢了怎么办 留服认证不了怎么办 无公害认证书怎么办 假学历认证报告怎么办 留学要求寄原件怎么办 干部小三怀孕怎么办? 小三的孩子怎么办 把小三打住院了怎么办 小月子没人伺候怎么办 寝室室友有狐臭怎么办 室友在寝室养猫怎么办 和直接领导不合怎么办 房产共有人去世怎么办 发现邪教宣传内容怎么办 说课时两个课时怎么办 投稿文章被拒绝怎么办 中立性细胞偏低怎么办? 孩子爱告状老师怎么办 高中学不好数学怎么办 想做老师应该怎么办 教师职称换学校怎么办 大四差选修学分怎么办 尔雅通识课学分没修满怎么办 大学全英文授课怎么办 小孩脑部有囊肿怎么办 防震期间门应该怎么办 在家待着没意思怎么办 人户分离上学怎么办 小孩上学没人接送怎么办 宝宝上学没人接送怎么办 上海浦东新区酒类许可证怎么办 金税盘里报税处理打不开怎么办 惠民卡到期了怎么办 遇到不拴狗链的主人怎么办 平安福没钱续保怎么办 提前很久到机场怎么办