类载入器

  虚拟机设计团队把类载入阶段中的“通过一个类的全限定名来获取描写叙述此类的二进制字节流”这个动作放到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. ORACLE 中如何截取到时间的年月日中的年

    在Oracle中,要获得日期中的年份,例如把sysdate中的年份取出来,并不是一件难事.常用的方法是:Select to_number(to_char(sysdate,'yyyy')) from d ...

  2. Selenium2+python自动化70-unittest之跳过用例(skip)

    前言 当测试用例写完后,有些模块有改动时候,会影响到部分用例的执行,这个时候我们希望暂时跳过这些用例. 或者前面某个功能运行失败了,后面的几个用例是依赖于这个功能的用例,如果第一步就失败了,后面的用例 ...

  3. LTE试题

    D 如果出现eNB的告警1018007“小区退服,光口不可用”,不可能是以下哪种原因造成的?(          ) 基带板上Ir接口光模块损坏 基带板上Ir接口光模块被拔出 基带板上Ir接口光模块型 ...

  4. eclipse 中添加自定义 classpath 的方法,以及 javac 和 java 的一些使用细节

    目标: 将 ~/java 加入classpath eclipse: 右键选中 Project, Properties, Java Build Path, Libraries, Add External ...

  5. iOS:视图切换的第一种方式:模态窗口

    一.UIModalController:模态窗口(一个控制器模态出另一个控制器的模态窗口) 当我们在view controller A中模态显示view controller B的时候,A就充当pre ...

  6. go语言基础之普通函数的调用流程

    函数调用流程:先调用后返回,先进后出,函数递归,函数调用自己本分,利用此物点 1.普通函数的调用流程 package main //必须 import "fmt" func fun ...

  7. Servlet学习笔记(二):表单数据

    很多情况下,需要传递一些信息,从浏览器到 Web 服务器,最终到后台程序.浏览器使用两种方法可将这些信息传递到 Web 服务器,分别为 GET 方法和 POST 方法. 1.GET 方法:GET 方法 ...

  8. 【C/C++】Linux C,关于刷新printf输出问题

    直接描述吧:int i = 0;while(1){printf("now i = %d\r", i);fflush(stdout);i++;sleep(1);}我想在输出中不仅仅是 ...

  9. public类型中internal成员

    今天遇到一问题,找到下面的两篇文章,研究比较深入,特转了一下, 最近除了搞ASP.NET MVC之外,我也在思考一些编程实践方面的问题.昨天在回家路上,我忽然对一个问题产生了较为清晰的认识.或者说,原 ...

  10. 同一个数据库实例,不同用户下多表创建视图,Hibernate完毕ORM映射,Spring整合,后台实现

    1.同一个数据库实例.同用户,多表创建视图 2.同一个数据库实例,不同用户下.多表创建视图 3.同一个数据库,不同数据库实例,多表创建视图 4.不同类型数据库,多表创建视图 1.同一个数据库实例.同用 ...