在jDK1.8中,Classloader加载class的字节码到JVM,它是遵循双亲委派模型的加载机制,主要是由BootstrapClassLoader,ExtClassLoader、AppClassloader,然后是自定义的Classloader加载,这样做主要是保证JDK的内部类加载安全性,所以优先加载JDK的ClassPath的jar包的类.双亲委派模型的如下图所示,其实就是由两级父Classloader加载,分别是BootstrapClassloader和ExtClassloader。

JDK的双亲委派模型

以下是Classloader的loadClass的和核心代码:

  1. 首先获取class的name的锁,是为了防止多线程并发的加载一个类,只有一个线程去加载,
  2. findLoadedClass获取JVM已经加载的缓存中该Classloader实例是否已经加载该类, JVM底层实际是SystemDictionary,其底层是HASH表实现。
  3. 如果JVM的缓存没有加载过这个className,则先交给parent加载器加载,

如果parent为空,则由BootstrapClassloader去加载,由于BootstrapClassloader是由C++实现,java没有这个类,所以通过JNI调用JVM的函数去加载。 4. 如果还没有加载到,则子类实现的findClass方法去加载。 5. 最后判断resolve参数,是否处理class的链接。 6. 最后返回class,没有加载到,由子类抛出ClassNotFoundException异常.

protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException
{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass<?> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {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}if (c == null) {// If still not found, then invoke findClass in order// to find the class.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;}
}

JDK 打破双亲委派模型的两种方式

继承ClassLoader,并重写loadClass

  1. 从JVM层的缓存查询当前Classloader是否加载过。
  2. 如果没有直接从本地资源查询是否有有对应的资源,如果有,则直接调用define进行加载到JVM,返回Class.
  3. 没有本地资源没有,则从parent的Classloader加载相应的资源.
  4. 最后返回Class.
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {System.out.println("load Class: " + name);synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass<?> c = findLoadedClass(name);if(c != null){return c;}byte[] bt = classNameToContent.get(name);if (bt != null) {try {return defineClass(name, bt, 0, bt.length);} catch (Exception e) {e.printStackTrace();}}try {if (getParent() != null) {c = getParent().loadClass(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}if (c != null) {return c;}}return null;
}

使用Thread的contextClassLoader

JDK中利用线程的contextClassloader去打破双亲委派模型的例子就是Serviceloader, Serviceloader是JDK里面全称是Service Provider Interface,是实现服务提供接口,实现插件的方式,例如JDBC的Driver就是使用JDK的SPI,具体实现是适配不同的数据库,由于Driver是在rt.jar是由BootstrapClassloader加载,而接口实现类是由第三方的jar包提供,所有BootstrapClassLoader就没有办法就在,所以JDK做了一个妥协, 委派给当前线程的contextloader去加载实现类
下面是ServiceLoader的load方法,可以看出是获取当亲线程的contextClassloader去加载的接口的实现类。当前主线程类加载器默认是AppClassloader加载器加载。这样就是违反了双亲委派模型.

public static <S> ServiceLoader<S> load(Class<S> service) {ClassLoader cl = Thread.currentThread().getContextClassLoader();return ServiceLoader.load(service, cl);
}

并行类加载机制

前面介绍Classloader的抽象类中loadClass方法,加载开始时,需要获取类全名的锁, 如果用到了并行类加载机制,就会用到. 如果需要使用并行类加载机制,只有再自定义的类加载加一个静态代码块中,增加下面一行。

static {ClassLoader.registerAsParallelCapable();
}

ClassLoader#registerAsParallelCapable

  1. 将自定义继承ClassLoader的类注册到ParallelLoaders的Set集合中.
protected static boolean registerAsParallelCapable() {Class<? extends ClassLoader> callerClass =Reflection.getCallerClass().asSubclass(ClassLoader.class);return ParallelLoaders.register(callerClass);
}

ParallelLoaders类中其实就是维护一个Classloader实现类的Set,其中元素都是调用registerAsParallelCapable注册为并行类加载的classloader,

private static class ParallelLoaders {private ParallelLoaders() {}// the set of parallel capable loader typesprivate static final Set<Class<? extends ClassLoader>> loaderTypes =Collections.newSetFromMap(new WeakHashMap<Class<? extends ClassLoader>, Boolean>());static {synchronized (loaderTypes) { loaderTypes.add(ClassLoader.class); }}/*** Registers the given class loader type as parallel capabale.* Returns {@code true} is successfully registered; {@code false} if* loader's super class is not registered.*/static boolean register(Class<? extends ClassLoader> c) {synchronized (loaderTypes) {if (loaderTypes.contains(c.getSuperclass())) {// register the class loader as parallel capable// if and only if all of its super classes are.// Note: given current classloading sequence, if// the immediate super class is parallel capable,// all the super classes higher up must be too.loaderTypes.add(c);return true;} else {return false;}}}/*** Returns {@code true} if the given class loader type is* registered as parallel capable.*/static boolean isRegistered(Class<? extends ClassLoader> c) {synchronized (loaderTypes) {return loaderTypes.contains(c);}}
}

Classloaer构造函数中,

  1. 如果Classloader注册过并行类加载器,则创建parallelLockMap的锁的HashMap,
private ClassLoader(Void unused, ClassLoader parent) {this.parent = parent;if (ParallelLoaders.isRegistered(this.getClass())) {parallelLockMap = new ConcurrentHashMap<>();package2certs = new ConcurrentHashMap<>();domains =Collections.synchronizedSet(new HashSet<ProtectionDomain>());assertionLock = new Object();} else {// no finer-grained lock; lock on the classloader instanceparallelLockMap = null;package2certs = new Hashtable<>();domains = new HashSet<>();assertionLock = this;}
}

Classloaer#getClassLoadingLock

  1. 在loadClass中获取类锁中,会判断parallelLockMap不为空,会创建一个Object对象作为这个classloader类的锁,然后放入hashMap中.

这样进行类加载过程,就synchonized锁就不是锁Classloader实例的this指针,而是Hashmap中获取加载类全名的锁,这样不同类全名就可以并行加载,这样减少了锁的粒度,提升类加载的速度.

protected Object getClassLoadingLock(String className) {Object lock = this;if (parallelLockMap != null) {Object newLock = new Object();lock = parallelLockMap.putIfAbsent(className, newLock);if (lock == null) {lock = newLock;}}return lock;
}

总结
本文主要就JDK的阐述了双亲委派模型以及JDk中打破双亲委派的两种方式,最后介绍了JDK中实现并行类加载的实现原理.

JAVA类加载机制之Classloader以及打破加载机制的方式相关推荐

  1. java类加载器顺序_java中类的加载顺序介绍(ClassLoader)

    1.ClassNotFoundExcetpion 我们在开发中,经常可以遇见java.lang.ClassNotFoundExcetpion这个异常,今天我就来总结一下这个问题.对于这个异常,它实质涉 ...

  2. java 类加载生命周期_Java类的加载与生命周期

    一.概要: 类的生命周期从类的 加载.连接.初始化 开始,到类的 卸载结束: 二.几个阶段: 加载:查找并加载类的二进制数据.(把类的.class文件的二进制数据读入内存,存放在运行时数据区的方法区: ...

  3. java 类加载器的理解及加载机制?

    通过 java 命令运行 java 程序的步骤就是指定包含 main 方法的完整类名以及一个 classpath 类路径,类路径可以有多个,对于直接的 class 文件路径就是 class 文件的根目 ...

  4. java类的加载机制简述

    1.JVM启动时,会申请内存空间,按功能划分,如下图 2.Java的类加载过程 一个Java文件从编码完成到最终执行,一般主要包括两个过程:编译和运行 . 编译,即把我们写好的java文件,通过jav ...

  5. Spring懒加载机制原理和配置讲解

    一.什么是懒加载 Spring默认会在容器初始化的过程中,解析xml或注解,创建配置为单例的bean并保存到一个map中,这样的机制在bean比较少时问题不大,但一旦bean非常多时,spring需要 ...

  6. Java中的类加载器(ClassLoader)及类的加载机制

    类加载器就是用来将class文件加载到内存中的一个java类! 系统默认有三个类加载器! ①BootStrap:这不是java类,是java虚拟机在启动的时候加载的第一个类,这个加载器用来加载核心类, ...

  7. Android开发之Java基础JVM和ClassLoader以及类加机制面试题

    在面试中被问到Java相关的东西非常多: 首先说下Java内存模型: 主要由: 程序计数器,Java虚拟机栈,本地方法栈=>被线程私有 方法区 堆组成=>被线程共有 Java类加载机制Cl ...

  8. java加载机制_详解Java类加载机制

    一:ClassLoader 从JVM结构图中可以看到,类加载器的作用是将Java类文件加载到Java虚拟机. HotSpot JVM结构,图片来自Java Garbage Collection Bas ...

  9. Java 类加载体系之 ClassLoader 双亲委托机制

    Java 类加载体系之 ClassLoader 双亲委托机制 java 是一种类型安全的语言,它有四类称为安全沙箱机制的安全机制来保证语言的安全性,这四类安全沙箱分别是: 类加载体系 .class文件 ...

最新文章

  1. VS asp.net 连接64位oracle 11g
  2. Android CardView 开发过程中要注意的细节
  3. LINK : fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏
  4. pyppeteer:比selenium更高效的爬虫利器
  5. python数据结构-栈和队列的实现
  6. sql 日期间隔排查双休_免费教程《图解SQL面试题》
  7. c# 指定打开某个路径下的CMD_Node.js 环境在 Windows 系统下安装与搭建教程
  8. 深入理解JVM(6)——JVM性能调优实战
  9. 快速学习nodejs系列:四、nodejs特性1--单线程
  10. 汇编语言王爽 实验七
  11. foremost的下载安装使用说明
  12. uCore lab1 操作系统实验
  13. rhel7-firewalld端口转发
  14. 智能识别系统设计---图像特征提取
  15. 【职场新贵】告诉你如何在压力下高效工作
  16. 【影音制作】编辑视频
  17. 【金猿技术展】同盾科技知识联邦技术——3.0人工智能的坚强基石
  18. 如何将多个excel表格合并成一个_如何将多个PDF文档合并为一个
  19. 网络摄像机产品介绍以及学习
  20. 百度地图使用的详细教程

热门文章

  1. python透明的桌面时钟_教你给手机桌面添加透明时钟,好看还实用
  2. web服务器稳定测试,web服务器测试脚本 - 漂浮 - 51Testing软件测试网 51Testing软件测试网-软件测试人的精神家园...
  3. git实战-2、gitk使用
  4. Unreal动画导入导出
  5. 手把手带你用Python完成一个能写进简历的项目(实战篇)
  6. 无线投影协作服务器,安全高效 开启无线协作新模式_巴可 CSE-200_投影机导购-中关村在线...
  7. 教程:使用PySpark和MapR沙盒
  8. PySimpleGUI库的查询小程序开发
  9. Python Flask微信小程序登录流程及登录api实现代码
  10. grafana结合zabbix打造炫酷监控界面