知道类加载的过程吗?


类加载过程:加载->链接->初始化。链接过程又可分为三步:验证->准备->解析java


那加载这一步作了什么?

类加载过程的第一步,主要完成下面3件事情:web

  1. 经过全类名获取定义此类的二进制字节流swift

  2. 将字节流所表明的静态存储结构转换为方法区的运行时数据结构数组

  3. 在内存中生成一个表明该类的 Class 对象,做为方法区这些数据的访问入口微信

虚拟机规范多上面这3点并不具体,所以是很是灵活的。好比:"经过全类名获取定义此类的二进制字节流" 并无指明具体从哪里获取、怎样获取。好比:比较常见的就是从 ZIP 包中读取(往后出现的JAR、EAR、WAR格式的基础)、其余文件生成(典型应用就是JSP)等等。数据结构

一个非数组类的加载阶段(加载阶段获取类的二进制字节流的动做)是可控性最强的阶段,这一步咱们能够去完成还能够自定义类加载器去控制字节流的获取方式(重写一个类加载器的 loadClass() 方法)。数组类型不经过类加载器建立,它由 Java 虚拟机直接建立。app

类加载器、双亲委派模型也是很是重要的知识点,这部份内容会在后面的问题中单独介绍到。编辑器

加载阶段和链接阶段的部份内容是交叉进行的,加载阶段还没有结束,链接阶段可能就已经开始了。ide

知道哪些类加载器?

JVM 中内置了三个重要的 ClassLoader,除了 BootstrapClassLoader 其余类加载器均由 Java 实现且所有继承自java.lang.ClassLoader源码分析

  1. BootstrapClassLoader(启动类加载器) :最顶层的加载类,由C++实现,负责加载 %JAVA_HOME%/lib目录下的jar包和类或者或被 -Xbootclasspath参数指定的路径中的全部类。

  2. ExtensionClassLoader(扩展类加载器) :主要负责加载目录 %JRE_HOME%/lib/ext 目录下的jar包和类,或被 java.ext.dirs 系统变量所指定的路径下的jar包。

  3. AppClassLoader(应用程序类加载器) :面向咱们用户的加载器,负责加载当前应用classpath下的全部jar包和类。

双亲委派模型知道吗?能介绍一下吗?

双亲委派模型介绍

每个类都有一个对应它的类加载器。系统中的 ClassLoder 在协同工做的时候会默认使用 双亲委派模型 。即在类加载的时候,系统会首先判断当前类是否被加载过。已经被加载的类会直接返回,不然才会尝试加载。加载的时候,首先会把该请求委派该父类加载器的 loadClass() 处理,所以全部的请求最终都应该传送到顶层的启动类加载器 BootstrapClassLoader 中。当父类加载器没法处理时,才由本身来处理。当父类加载器为null时,会使用启动类加载器 BootstrapClassLoader 做为父类加载器。

每一个类加载都有一个父类加载器,咱们经过下面的程序来验证。

public class ClassLoaderDemo { public static void main(String[] args) { System.out.println("ClassLodarDemo's ClassLoader is " + ClassLoaderDemo.class.getClassLoader()); System.out.println("The Parent of ClassLodarDemo's ClassLoader is " + ClassLoaderDemo.class.getClassLoader().getParent()); System.out.println("The GrandParent of ClassLodarDemo's ClassLoader is " + ClassLoaderDemo.class.getClassLoader().getParent().getParent()); }}

Output

ClassLodarDemo's ClassLoader is sun.misc.Launcher$AppClassLoader@18b4aac2The Parent of ClassLodarDemo's ClassLoader is sun.misc.Launcher$ExtClassLoader@1b6d3586The GrandParent of ClassLodarDemo's ClassLoader is null

AppClassLoader的父类加载器为ExtClassLoader ExtClassLoader的父类加载器为null,null并不表明ExtClassLoader没有父类加载器,而是 Bootstrap ClassLoader 。

其实这个双亲翻译的容易让别人误解,咱们通常理解的双亲都是父母,这里的双亲更多地表达的是“父母这一辈”的人而已,并非说真的有一个 Mather ClassLoader 和一个 Father ClassLoader 。另外,类加载器之间的“父子”关系也不是经过继承来体现的,是由“优先级”来决定。官方API文档对这部分的描述以下:

The Java platform uses a delegation model for loading classes. The basic idea is that every class loader has a "parent" class loader. When loading a class, a class loader first "delegates" the search for the class to its parent class loader before attempting to find the class itself.

双亲委派模型实现源码分析

双亲委派模型的实现代码很是简单,逻辑很是清晰,都集中在 java.lang.ClassLoader 的 loadClass() 中,相关代码以下所示。

private final ClassLoader parent; protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // 首先,检查请求的类是否已经被加载过 Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) {//父加载器不为空,调用父加载器loadClass()方法处理 c = parent.loadClass(name, false); } else {//父加载器为空,使用启动类加载器 BootstrapClassLoader 加载 c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { //抛出异常说明父类加载器没法完成加载请求 }
if (c == null) { long t1 = System.nanoTime(); //本身尝试加载 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; } }
双亲委派模型带来了什么好处呢?

双亲委派模型保证了Java程序的稳定运行,能够避免类的重复加载(JVM 区分不一样类的方式不只仅根据类名,相同的类文件被不一样的类加载器加载产生的是两个不一样的类),也保证了 Java 的核心 API 不被篡改。若是不用没有使用双亲委派模型,而是每一个类加载器加载本身的话就会出现一些问题,好比咱们编写一个称为 java.lang.Object 类的话,那么程序运行的时候,系统就会出现多个不一样的 Object 类。

若是咱们不想用双亲委派模型怎么办?

为了不双亲委托机制,咱们能够本身定义一个类加载器,而后重载 loadClass() 便可。

如何自定义类加载器?

除了 BootstrapClassLoader 其余类加载器均由 Java 实现且所有继承自java.lang.ClassLoader。若是咱们要自定义本身的类加载器,很明显须要继承 ClassLoader


   
你点的每一个“在看”,我都当成了喜欢

本文分享自微信公众号 - Java学习提高(javaxuexitisheng)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索