jvm之类加载器

程序装载过程

在这里插入图片描述

类加载器类型

  1. java虚拟机自带的加载器
  • 根类加载器
  • 扩展类加载器
  • 系统类加载器
  1. 用户自定义加载器
  • java.lang.ClassLoader的子类
  • 用户可以定制类的加载方式

每一个类对象中都会有对应的加载器的引用,子加载器所加载的类能够访问父加载器加载的类,反过来不行(双亲委托)。

三大类加载器所加载的路径范围:

public class MyTest18 {
    public static void main(String[] args) {
        System.out.println(System.getProperty("sun.boot.class.path"));//系统类加载器加载路径
        System.out.println(System.getProperty("java.ext.dirs"));//扩展类加载器加载路径
        System.out.println(System.getProperty("java.class.path"));//应用类加载器加载过程
    }
}

结果:

F:\development\java\jdk\jre\lib\resources.jar;F:\development\java\jdk\jre\lib\rt.jar;F:\development\java\jdk\jre\lib\sunrsasign.jar;F:\development\java\jdk\jre\lib\jsse.jar;F:\development\java\jdk\jre\lib\jce.jar;F:\development\java\jdk\jre\lib\charsets.jar;F:\development\java\jdk\jre\lib\jfr.jar;F:\development\java\jdk\jre\classes
F:\development\java\jdk\jre\lib\ext;C:\Windows\Sun\Java\lib\ext
F:\development\java\jdk\jre\lib\charsets.jar;F:\development\java\jdk\jre\lib\deploy.jar;F:\development\java\jdk\jre\lib\ext\access-bridge-64.jar;F:\development\java\jdk\jre\lib\ext\cldrdata.jar;F:\development\java\jdk\jre\lib\ext\dnsns.jar;F:\development\java\jdk\jre\lib\ext\jaccess.jar;F:\development\java\jdk\jre\lib\ext\jfxrt.jar;F:\development\java\jdk\jre\lib\ext\localedata.jar;F:\development\java\jdk\jre\lib\ext\nashorn.jar;F:\development\java\jdk\jre\lib\ext\sunec.jar;F:\development\java\jdk\jre\lib\ext\sunjce_provider.jar;F:\development\java\jdk\jre\lib\ext\sunmscapi.jar;F:\development\java\jdk\jre\lib\ext\sunpkcs11.jar;F:\development\java\jdk\jre\lib\ext\zipfs.jar;F:\development\java\jdk\jre\lib\javaws.jar;F:\development\java\jdk\jre\lib\jce.jar;F:\development\java\jdk\jre\lib\jfr.jar;F:\development\java\jdk\jre\lib\jfxswt.jar;F:\development\java\jdk\jre\lib\jsse.jar;F:\development\java\jdk\jre\lib\management-agent.jar;F:\development\java\jdk\jre\lib\plugin.jar;F:\development\java\jdk\jre\lib\resources.jar;F:\development\java\jdk\jre\lib\rt.jar;F:\work\java\javaCode\trivial\test\production\untitled;F:\development\IntelliJ IDEA 2018.3.5\lib\idea_rt.jar

双亲委托机制

在这里插入图片描述
为什么要有双亲委托机制?

  1. 可以确保Java核心库的类型安全:所有的Java应用都至少会引用java.lang.Object类,也就是说在运行期,java.lang.Object这个类会被加载到Java虚拟机;如果用户自定义的类加载器可以加载Object类,那么很可能就会在JVM中存在多个版本的java.lang.Object类,而且这些类之间还是不兼容的,相互不可见的(正是命名空间在发挥着作用)。借助于双亲委托机制,Java核心类库中的类加载工作都是由启动类加载器来同意完成加载工作,从而确保了Java应用所使用的都是同一个版本的Java核心类库,他们之间是相互兼容的;

  2. 可以确保Java核心类库所提供的类不会被自定义的类所替代。假设我们自己定义了一个java.lang.Object,这个类是无法被加载到JVM中的,因为系统启动时,首先会由启动类加载器加载rt.jar包中的java.lang.Object,之后我们的类在被加载时会因为名字相同而被认为是已经加载过了,不会进行加载。

装载器相关函数

  • 获取:
clazz.getClassLoader();//获取当前classLoader
Thread.currentThread().getContextClassLoader();//获取当前线程上下文classLoader
ClassLoader.getSystemClassLoader();

Thread.currentThread().getContextClassLoader() 和 Class.getClassLoader()区别

打个简单的比方,你一个WEB程序,发布到Tomcat里面运行。
首先是执行Tomcat org.apache.catalina.startup.Bootstrap类,这时候的类加载器是ClassLoader.getSystemClassLoader()。
而我们后面的WEB程序,里面的jar、resources都是由Tomcat内部来加载的,所以你在代码中动态加载jar、资源文件的时候,首先应该是使用Thread.currentThread().getContextClassLoader()。如果你使用Test.class.getClassLoader(),可能会导致和当前线程所运行的类加载器不一致(因为Java天生的多线程)。
Test.class.getClassLoader()一般用在getResource,因为你想要获取某个资源文件的时候,这个资源文件的位置是相对固定的。

  • 读取
classLoader.loadClass("com.test.Hello");//不会初始化
Class.forName("com.test.Hello");//会初始化

loadClass源码过程:

  1. 使用指定的二进制名称来加载类,这个方法的默认实现按照以下顺序查找类:
  2. 调用findLoadedClass(String)方法检查这个类是否被加载过
  3. 使用父加载器调用loadClass(String)方法,如果父加载器为Null,类加载器装载虚拟机内置的加载器调用findClass(String)方法装载类,如果,按照以上的步骤成功的找到对应的类,并且该方法接收的resolve参数的值为true,那么就调用resolveClass(Class)方法来处理类。
    ClassLoader的子类最好覆盖findClass(String)而不是这个方法。

命名空间

每一个类加载器都有一个独立的类名称空间,就是比较两个类是否相等,首先他们是由同一个类加载器加载的情况下才能比较;否则即使两个类来自同一个 Class 文件,由同一个虚拟机进行加载(但是使用的类加载器不同),这两个类肯定不相同。

  • 每个类加载器都有自己的命名空间,命名空间由该加载器及所有父加载器所加载的类构成。
  • 在同一个命名空间中,不会出现类的完整名字(包括类的包名)相同的两个类。
  • 在不同的命名空间中,有可能会出现类的完整名字(包括类的包名)相同的两个类。(可以被加载多次)
  • 同一命名空间内的类是互相可见的(但是相互能否访问是由权限修饰符决定),非同一命名空间内的类是不可见的。
  • 子加载器加载的类可以见到父加载器加载的类,父加载器加载的类不能见到子加载器加载的类。
  • 如果两个加载器之间没有直接或者间接的父子关系,那么它们相互各自加载的类相互不可见。

双亲委托机制的破坏

线程上下文类加载器

在使用jdbc时,直接一句话便可以获取mysql的接口,其原理是什么呢?

con=DriverManager.getConnection("jdbc:mysql://localhost:3306/tsetjdbc","root", "123456");

在调用静态方法时,会触发DriverManager的静态代码块

static {
    loadInitialDrivers();
    println("JDBC DriverManager initialized");
}
private static void loadInitialDrivers() {
	String drivers;
	AccessController.doPrivileged(new PrivilegedAction<Void>() {
	public Void run() {
	
	ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
	Iterator<Driver> driversIterator = loadedDrivers.iterator();
	try{
		while(driversIterator.hasNext()) {
			driversIterator.next();
		}
	} catch(Throwable t) {
	    // Do nothing
	    }
	    return null;
	}
});

Driver.class在rt.jar包中,是数据库操作的接口,是启动类加载器进行加载的。然而在加载时候,具体实现是由系统加载器加载,这时启动类加载器加载的类无法使用系统加载器加载加载的类。所以需要用ClassLoader calssLoader=Thread.currnetThread().getContextClassLoader();打破双亲委托机制。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值