类载入器

  虚拟机设计团队把类载入阶段中的“通过一个类的全限定名来获取描写叙述此类的二进制字节流”这个动作放到Java虚拟机外部去实现。以便让应用程序自己决定怎样去获取所须要的类。实现这个动作的代码模块称为“类载入器”。


类载入器层次(等级)

  从JVM的角度来讲,仅仅存在两种不同的类载入器。

  第一类是启动类载入器(Bootstrap ClassLoader):这个类载入器主要载入JVM自身工作须要的类。这个类载入器由C++语言实现(特指HotSpot)。是虚拟机自身的一部分。

负责将存放在%JAVA_HOME%\lib文件夹中的,或者被-Xbootclasspath參数所指定的路径中。而且是虚拟机识别的(仅依照文件名称识别。如rt.jar,名字不符合的类库即使放在lib文件夹中也不会被载入)类载入到虚拟机内存中。

  还有一类就是全部其它的类载入器,这些载入器都是由java实现,独立于虚拟机外部。

  Extension ClassLoader:这个类即在其有sun.misc.Launcher$ExtClassLoader实现,它负责载入%JAVA_HOME%\lib\ext文件夹中,或者被java.ext.dirs系统变量所指定的路径中的全部类库,开发人员能够直接使用扩展类载入器。

  Application ClassLoader:这个类载入器由sun.misc.Launcher$AppClassLoader实现。因为这个类载入器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也成它为系统类载入器。它负责载入用户类路径上指定的类库,开发人员能够直接使用这个类载入器。假设应用程序中没有自己定义过自己的类载入器。普通情况下这个就是程序中默认的类载入器。

  AppClassLoader的parent是ExtClassLoader。

非常多文章在介绍ClassLoader层次的结构时把Bootstrap ClassLoader也列在ExtClassLoader的上一级中。事实上Bootstrap ClassLoader并不属于JVM的类等级层次。因为Bootstrap ClassLoader没有遵守ClassLoader的载入股则。另外Bootstrap ClassLoader没有子类,ExtClassLoader的父类也不是Bootstrap ClassLoader,ExtClassLoader并没有父类,我们在应用中能提取到的顶层父类是ExtClassLoader.

代码举例:

        ClassLoader cl = Thread.currentThread().getContextClassLoader();
System.out.println(cl.toString());
System.out.println(cl.getParent());
System.out.println(cl.getParent().getParent());

  输出结果:

sun.misc.Launcher$AppClassLoader@7dd74c03
sun.misc.Launcher$ExtClassLoader@41bf9980
null

怎样获得ClassLoader

  1. this.getClass().getClassLoader();//使用当前类的ClassLoader
  2. Thread.currentThread().getContextClassLoader();//使用当前线程的ClassLoader
  3. ClassLoader.getSystemClassLoader();//使用系统ClassLoader

  代码举例:

    public void test()
{
System.out.println(this.getClass().getClassLoader().toString());
System.out.println(Thread.currentThread().getContextClassLoader());
System.out.println(ClassLoader.getSystemClassLoader());
}

  输出:

sun.misc.Launcher$AppClassLoader@41bf9980
sun.misc.Launcher$AppClassLoader@41bf9980
sun.misc.Launcher$AppClassLoader@41bf9980

[Tips]怎样获得类的class属性:

1. Class.forName(类路径全名);

2. this.getClass();

3. 使用.class,比方String.class

4. 对于基本数据类型有:Class c1 = int.class(class仅仅是约定标记。不是成员属性) 或者Class c2 = Integer.TYPE

  JVM载入class文件到内存有两种方式:

  1. 隐式载入:所谓的隐式载入就是不通过在代码里调用ClassLoader来记载须要的类。而是通过JVM来自己主动载入须要的类到内存的方式。

比如,当我们在类中集成或者引用某个类是,JVM在解析当前这个类时发现引用的类不在内存中,那么就会自己主动将这些类载入到内存中。

  2. 显示载入:相反的显示载入就是我们在代码中通过调用ClassLoader类来载入一个类的方式,比如,调用this.getClass.getClassLoader().loadClass()或者Class.forName()。或者我们自己实现的ClassLoader的findClass()方法等。


双亲委派模型

  双亲委派模型要求畜类鼎城的启动类载入器(Bootstrap ClassLoader)之外。其余的类载入器都应当有自己的父类载入器。这里类载入器之间的父子关系一般不会以inheritance的关系实现而是都是使用组合Composition关系来复用父类载入器的代码

  双亲委派模型不是一个强制性的约束模型,而是java设计者推荐给开发人员的一种类载入器实现方式。在java的世界中大部分的类载入器都遵循这个模型,但也有例外。到眼下为止。双亲委派模型主要出现过3此较大的“被破坏”情况,这个稍后再阐述。

  双亲委派模型的工作过程是:假设一个类载入器收到了类载入的请求,它首先不会自己去尝试载入这个类,而是把这个请求委派给父类载入器去完毕,每个层次的类载入器都是如此。因此全部的载入请求终于都应该传送到顶层的启动类载入器中。仅仅有当父载入器反馈自己无法完毕这个载入请求(它搜索范围中没有找到所需的类)时,子载入器才会尝试自己去载入。

  双亲托付机制的作用是防止系统jar包被本地替换,因为查找方法过程都是从最底层開始查找。 因此,一般我们自己定义的classloader都须要採用这样的机制,我们仅仅须要继承java.lang.ClassLoader实现findclass就可以,假设须要很多其它控制,自己定义的classloader就须要重写loadClass方法了,比方tomcat的载入过程,这个比較复杂。能够通过其它文档资料查看相关介绍。

  双亲委派模型对于保证java程序的稳定运作非常重要,实现体如今java.lang.ClassLoader的loadClass()方法中。例如以下:

protected Class<?

> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class 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 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;
}
}

  loadClass()方法的载入步骤为:

  1. 调用 Class c = findLoadedClass(name);来检查是否已经载入类;

  2. 在父类载入器上调用loadClass方法:

   c = parent.loadClass(name, false);

  假设父类载入器为null,则使用虚拟机的内置类载入器:

   c = findBootstrapClassOrNull(name);

  3. 在父类载入器无法载入的时候再调用本身的findClass方法来进行类载入:

   c = findClass(name);

每个ClassLoader载入Class的过程是:

1.检測此Class是否载入过(即在cache中是否有此Class),假设有到8,假设没有到2;

2.假设parent classloader不存在(没有parent。那parent一定是bootstrap classloader了),到4;

3.请求parent classloader载入。假设成功到8,不成功到5;

4.请求jvm从bootstrap classloader中载入。假设成功到8;

5.寻找Class文件(从与此classloader相关的类路径中寻找)。假设找不到则到7;

6.从文件里载入Class。到8;

7.抛出ClassNotFoundException;

8.返回Class.


破坏双亲委派模型

  上文提到了到眼下为止,双亲委派模型出现过3次较大规模的“被破坏”的情况,这里详细阐述一下。

  第一次。

发生在双亲委派模型出现之前(jdk1.2公布之前)。因为双亲委派模型在jdk1.2之后才被引入。而类载入器和抽象类java.lang.ClassLoader则在jdk1.0时代就已经存在,面对已经存在的用户自己定义类载入器的实现代码,java设计者引入双亲委派模型时不得不做出了一些妥协。

历史已经成为过去,详细的在此不赘述。须要注意的是jdk1.2之后不提倡用户再去覆盖loadClass()方法,而应当把自己的类载入逻辑写到findClass()中,这样保证符合双亲委派模型的规则。

  第二次。

由模型本身的缺陷所导致的,双亲委派模型非常好地攻克了各个类载入器的基础类的统一问题。当父类载入器须要请求子类载入器去完毕类载入动作,比方JNDI服务:它的代码由启动类载入器去载入。但JNDI的目的是对资源进行集中管理和查找。它须要调用由独立厂商实现并部署在应用程序的Classpath下的JNDI提供者(SPI)的代码,可是启动类载入器不“认识”这些代码。

这是就要用到了线程上下文载入器(Thread Context ClassLoader)。这个类载入器能够通过java.lang.Thread类的setContextClassLoader()方法进行设置。

  这里怎么又出来一个context classloader呢?它有什么用呢?我们在建立一个线程Thread的时候,能够为这个线程通过setContextClassLoader方法来指定一个合适的classloader作为这个线程的context classloader,当此线程执行的时候,我们能够通过getContextClassLoader方法来获得此context classloader,就能够用它来载入我们所须要的Class。

默认的是system classloader。利用这个特性,我们能够“打破”classloader托付机制了。父classloader能够获得当前线程的context classloader。而这个context classloader能够是它的子classloader或者其它的classloader,那么父classloader就能够从其获得所需的 Class,这就打破了仅仅能向父classloader请求的限制了。这个机制能够满足当我们的classpath是在执行时才确定,并由定制的 classloader载入的时候,由system classloader(即在jvm classpath中)载入的class能够通过context classloader获得定制的classloader并载入入特定的class(一般是抽象类和接口,定制的classloader中是事实上现),比如web应用中的servlet就是用这样的机制载入的.

  第三次。

因为用户对程序动态性的追求而导致的,这里所说的“动态性”指的是当前一些非常“热门”的名称:代码热替换、模块热部署等,相似于鼠标键盘热拔插。

详细的能够看一下OSGi。在OSGi环境下。类载入器不再是双亲委派模型中的树型结构,而是一种网状结构。详细的能够翻阅一些相关资料。

Java类载入器(一)——类载入器层次与模型的更多相关文章

  1. java动态载入指定的类或者jar包反射调用其方法

    序言 有时候.项目中会用到java动态载入指定的类或者jar包反射调用其方法来达到模块的分离,使各个功能之间耦合性大大减少,更加的模块化.代码利用率更高.模式中的代理模式就用到java的这一机制. 下 ...

  2. java 27 - 1 反射之 类的加载器

    说到反射,首先说类的加载器. 类的加载: 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化. 加载: 就是指将class文件读入内存,并为之 ...

  3. java中三个类别加载器的关系以及各自加载的类的范围

    Java在需要使用类别的时候,才会将类别加载,Java的类别载入是由类别载入器(Class loader)来达到的,预设上,在程序启动之后,主要会有三个类别加载器:Bootstrap Loader.E ...

  4. 乐字节Java反射之三:方法、数组、类加载器和类的生命周期

    本文承接上一篇:乐字节Java发射之二:实例化对象.接口与父类.修饰符和属性 继续讲述Java反射之三:方法.数组.类加载器 一.方法 获取所有方法(包括父类或接口),使用Method即可. publ ...

  5. ClassLoader载入指定的类需注意六个细节或报ClassNotFundEception异常总结

    项目中,载入指定的类反射调用方法一直报类找不到,经过数百次的測试.对这样的问题有了一个又一次的认识,特总结.记录.分享例如以下: 1.路径中尽可能用"/"或者File.separa ...

  6. 反射(学习整理)----Class类和加载器ClassLoader类的整理

    1.学习反射的时整理的笔记!Class类和ClassLoader类的简单介绍 反射机制中的Class Class内部到底有什么呢?看下图! 代码: Class cls=Person.class; .C ...

  7. SpringMVC 学习-拦截器 HandlerInterceptor 类

    一.拦截器 HandlerInterceptor 类的作用 SpringMVC 的拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理. 二.怎么使用呢? 1. ...

  8. JVM 类的生命周期、类加载器

    类的加载.连接与初始化                  • 1. 加载:查找并加载类的二进制数据         • 2. 连接             – 2.1 验证:确保被加载的类的正确性   ...

  9. 第42天学习打卡(Class类 Class类的常用方法 内存分析 类的加载过程 类加载器 反射操作泛型 反射操作注解)

    Class类 对象照镜子后得到的信息:某个类的属性.方法和构造器.某个类到底实现了哪些接口.对于每个类而言,JRE都为其保留一个不变的Class类型的对象.一个Class对象包含了特定某个结构(cla ...

随机推荐

  1. EatCam Webcam Recorder Pro

    EatCam Webcam Recorder Pro Webcam Recorder records webcams to AVI, FLV, WMV files and watch them whe ...

  2. 提交svn的时候,提示丢失了预定增加的xxxx

    百度之,发现都是linux或者命令行下的解决方法, 经测试,右键svn目录 选择tortoisesvn 选择svn还原, 即可解决问题

  3. .NET:C#的匿名委托 和 Java的匿名局部内部类

    背景 这几天重温Java,发现Java在嵌套类型这里提供的特性比较多,结合自身对C#中匿名委托的理解,我大胆的做了一个假设:Java的字节码只支持静态嵌套类,内部类.局部内部类和匿名局部内部类都是编译 ...

  4. pytest文档1-环境准备与入门

    前言 首先说下为什么要学pytest,在此之前相信大家已经掌握了python里面的unittest单元测试框架,那再学一个框架肯定是需要学习时间成本的. 刚开始我的内心是拒绝的,我想我用unittes ...

  5. Source Insight 源代码查看工具

    在开发的过程中,有时候我们需要研究源代码,查看源码是一个好的习惯,能帮我们学到很多的东西,比如JDK可以帮助我们理解很多设计模式在实际开发中的应用,又或者android开发者,源代码更是必不可少的,当 ...

  6. nmap常用扫描命令

    NMap,也就是Network Mapper,是Linux下的网络扫描和嗅探工具包. nmap是在网络安全渗透测试中经常会用到的强大的扫描器.功能之强大,不言而喻.下面介绍一下它的几种扫描命令.具体的 ...

  7. c++ 读写Excel及数据导入SQLServer

    c++ 读写Excel及数据导入SQLServer                 C++操作Excel ,网上的资料还是比较多的,写这篇文章也是分享给初学者一些经验. 本人 觉得CSpreadShe ...

  8. Tensorflow 之 name/variable_scope 变量管理

    name/variable_scope 的作用 充分理解 name / variable_scope TensorFlow 入门笔记 当一个神经网络比较复杂.参数比较多时,就比较需要一个比较好的方式来 ...

  9. java学习笔记2--数据类型、数组

    本文地址:http://www.cnblogs.com/archimedes/p/java-study-note2.html,转载请注明源地址. 1.数据类型 Java数据类型有: 原始数据类型(Pr ...

  10. windows10 onenote 显示英文 怎么办?

    转自:https://jingyan.baidu.com/article/fdffd1f85d064df3e98ca108.html 原因:首选输入法为英文. 方法:修改,调整首选为中文. 1.右下角 ...