Java虚拟机类载入过程是把Class类文件载入到内存。并对Class文件里的数据进行校验、转换解析和初始化,终于形成能够被虚拟机直接使用的java类型的过程。

在载入阶段,java虚拟机须要完毕下面3件事:

a.通过一个类的全限定名来获取定义此类的二进制字节流。

b.将定义类的二进制字节流所代表的静态存储结构转换为方法区的执行时数据结构。

c.在java堆中生成一个代表该类的java.lang.Class对象,作为方法区数据的訪问入口。

Java虚拟机的类载入是通过类载入器实现的, Java中的类载入器体系结构例如以下:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center">

(1).BootStrap ClassLoader:启动类载入器。负责载入存放在%JAVA_HOME%\lib文件夹中的,或者通被-Xbootclasspath參数所指定的路径中的。而且被java虚拟机识别的(仅依照文件名称识别。如rt.jar,名字不符合的类库,即使放在指定路径中也不会被载入)类库到虚拟机的内存中,启动类载入器无法被java程序直接引用。

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

(3).Application ClassLoader:应用程序类载入器,由sun.misc.Launcher$AppClassLoader实现。负责载入用户类路径classpath上所指定的类库。是类载入器ClassLoader中的getSystemClassLoader()方法的返回值,开发人员能够直接使用应用程序类载入器,假设程序中没有自己定义过类载入器,该载入器就是程序中默认的类载入器。

注意:上述三个JDK提供的类载入器尽管是父子类载入器关系,可是没有使用继承,而是使用了组合关系。

从JDK1.2開始。java虚拟机规范推荐开发人员使用双亲委派模式(ParentsDelegation Model)进行类载入,其载入步骤例如以下:

(1).假设一个类载入器收到了类载入请求,它首先不会自己去尝试载入这个类,而是把类载入请求委派给父类载入器去完毕。

(2).每一层的类载入器都把类载入请求委派给父类载入器,直到全部的类载入请求都应该传递给顶层的启动类载入器。

(3).假设顶层的启动类载入器无法完毕载入请求,子类载入器尝试去载入,假设连最初发起类载入请求的类载入器也无法完毕载入请求时,将会抛出ClassNotFoundException。而不再调用其子类载入器去进行类载入。

双亲委派 模式的类载入机制的长处是java类它的类载入器一起具备了一种带优先级的层次关系。越是基础的类。越是被上层的类载入器进行载入,保证了java程序的稳定执行。双亲委派模式的实现:

  1. protected synchronized Class<?

    > loadClass(String name, Boolean resolve) throws ClassNotFoundException{

  2. //首先检查请求的类是否已经被载入过
  3. Class c = findLoadedClass(name);
  4. if(c == null){
  5. try{
  6. if(parent != null){//委派父类载入器载入
  7. c = parent.loadClass(name, false);
  8. }
  9. else{//委派启动类载入器载入
  10. c = findBootstrapClassOrNull(name);
  11. }
  12. }catch(ClassNotFoundException e){
  13. //父类载入器无法完毕类载入请求
  14. }
  15. if(c == null){//本身类载入器进行类载入
  16. c = findClass(name);
  17. }
  18. }
  19. if(resolve){
  20. resolveClass(c);
  21. }
  22. return c;
  23. }
protected synchronized Class<?

> loadClass(String name, Boolean resolve) throws ClassNotFoundException{
//首先检查请求的类是否已经被载入过
Class c = findLoadedClass(name);
if(c == null){
try{
if(parent != null){//委派父类载入器载入
c = parent.loadClass(name, false);
}
else{//委派启动类载入器载入
c = findBootstrapClassOrNull(name);
}
}catch(ClassNotFoundException e){
//父类载入器无法完毕类载入请求
}
if(c == null){//本身类载入器进行类载入
c = findClass(name);
}
}
if(resolve){
resolveClass(c);
}
return c;
}

若要实现自己定义类载入器,仅仅须要继承java.lang.ClassLoader 类。而且重写其findClass()方法就可以。java.lang.ClassLoader 类的基本职责就是依据一个指定的类的名称,找到或者生成其相应的字节代码,然后从这些字节代码中定义出一个 Java 类,即 java.lang.Class 类的一个实例。除此之外,ClassLoader 还负责载入 Java 应用所需的资源,如图像文件和配置文件等。ClassLoader中与载入类相关的方法例如以下:

方法

说明

getParent()

返回该类载入器的父类载入器。

loadClass(String name)

载入名称为 二进制名称为name 的类。返回的结果是 java.lang.Class 类的实例。

findClass(String name)

查找名称为 name 的类,返回的结果是 java.lang.Class 类的实例。

findLoadedClass(String name)

查找名称为 name 的已经被载入过的类。返回的结果是 java.lang.Class 类的实例。

resolveClass(Class<?> c)

链接指定的 Java 类。

注意:在JDK1.2之前。类载入尚未引入双亲委派模式,因此实现自己定义类载入器时经常重写loadClass方法,提供双亲委派逻辑。从JDK1.2之后,双亲委派模式已经被引入到类载入体系中。自己定义类载入器时不须要在自己写双亲委派的逻辑,因此不鼓舞重写loadClass方法,而推荐重写findClass方法。

在Java中。随意一个类都须要由载入它的类载入器和这个类本身一同确定其在java虚拟机中的唯一性,即比較两个类是否相等。仅仅有在这两个类是由同一个类载入器载入的前提之下才有意义,否则,即使这两个类来源于同一个Class类文件,仅仅要载入它的类载入器不同样,那么这两个类必然不相等(这里的相等包含代表类的Class对象的equals()方法、isAssignableFrom()方法、isInstance()方法和instanceofkeyword的结果)。

样例代码例如以下:

  1. package com.test;
  2. public class ClassLoaderTest {
  3. public static void main(String[] args)throws Exception{
  4. //匿名内部类实现自己定义类载入器
  5. ClassLoader myClassLoader = new ClassLoader(){
  6. protected Class<?> findClass(String name)throws ClassNotFoundException{
  7. //获取类文件名称
  8. String filename = name.substring(name.lastIndexOf(“.”) + 1) + “.class”;
  9. InputStream in = getClass().getResourceAsStream(filename);
  10. if(in == null){
  11. throw RuntimeException(“Could not found class file:” + filename);
  12. }
  13. byte[] b = new byte[in.available()];
  14. return defineClass(name, b, 0, b.length);
  15. }catch(IOException e){
  16. throw new ClassNotFoundException(name);
  17. }
  18. };
  19. Object obj = myClassLoader.loadClass(“com.test.ClassLoaderTest”).newInstance();
  20. System.out.println(obj.getClass());
  21. System.out.println(obj instanceof com.test. ClassLoaderTest);
  22. }
  23. }
package com.test;

public class ClassLoaderTest {
public static void main(String[] args)throws Exception{
//匿名内部类实现自己定义类载入器
ClassLoader myClassLoader = new ClassLoader(){
protected Class<? > findClass(String name)throws ClassNotFoundException{
//获取类文件名称
String filename = name.substring(name.lastIndexOf(“.”) + 1) + “.class”;
InputStream in = getClass().getResourceAsStream(filename);
if(in == null){
throw RuntimeException(“Could not found class file:” + filename);
}
byte[] b = new byte[in.available()];
return defineClass(name, b, 0, b.length);
}catch(IOException e){
throw new ClassNotFoundException(name);
}
};
Object obj = myClassLoader.loadClass(“com.test.ClassLoaderTest”).newInstance();
System.out.println(obj.getClass());
System.out.println(obj instanceof com.test. ClassLoaderTest);
}
}

输出结果例如以下:

com.test.ClassLoaderTest

false

之所以instanceof会返回false。是由于com.test.ClassLoaderTest类默认使用Application ClassLoader载入,而obj是通过自己定义类载入器载入的。类载入不同样,因此不相等。

类载入器双亲委派模型是从JDK1.2以后引入的。而且仅仅是一种推荐的模型。不是强制要求的,因此有一些没有遵循双亲委派模型的特例:

(1).在JDK1.2之前,自己定义类载入器都要覆盖loadClass方法去实现载入类的功能,JDK1.2引入双亲委派模型之后,loadClass方法用于委派父类载入器进行类载入。仅仅有父类载入器无法完毕类载入请求时才调用自己的findClass方法进行类载入,因此在JDK1.2之前的类载入的loadClass方法没有遵循双亲委派模型。因此在JDK1.2之后。自己定义类载入器不推荐覆盖loadClass方法。而仅仅须要覆盖findClass方法就可以。

(2).双亲委派模式非常好地攻克了各个类载入器的基础类统一问题,越基础的类由越上层的类载入器进行载入,可是这个基础类统一有一个不足,当基础类想要调用回下层的用户代码时无法委派子类载入器进行类载入。为了解决问题JDK引入了ThreadContext线程上下文,通过线程上下文的setContextClassLoader方法能够设置线程上下文类载入器。

JavaEE仅仅是一个规范,sun公司仅仅给出了接口规范,详细的实现由各个厂商进行实现,因此JNDI。JDBC,JAXB等这些第三方的实现库就能够被JDK的类库所调用。

线程上下文类载入器也没有遵循双亲委派模型。

(3).近年来的热码替换,模块热部署等应用要求不用重新启动java虚拟机就能够实现代码模块的即插即用。催生了OSGi技术。在OSGi中类载入器体系被发展为网状结构。OSGi也没有全然遵循双亲委派模型。

Java虚拟机的类载入机制的更多相关文章

  1. JVM系列文章(四):类载入机制

    作为一个程序猿,只知道怎么用是远远不够的. 起码,你须要知道为什么能够这么用.即我们所谓底层的东西. 那究竟什么是底层呢?我认为这不能一概而论.以我如今的知识水平而言:对于Web开发人员,TCP/IP ...

  2. 深入研究Java类载入机制

    深入研究Java类载入机制   类载入是Java程序运行的第一步,研究类的载入有助于了解JVM运行过程,并指导开发人员採取更有效的措施配合程序运行. 研究类载入机制的第二个目的是让程序能动态的控制类载 ...

  3. 深入java虚拟机学习 -- 类的加载机制

    当看到"类的加载机制",肯定很多人都在想我平时也不接触啊,工作中无非就是写代码,不会了可以百度,至于类,jvm是怎么加载的我一点也不需要关心.在我刚开始工作的时候也觉得这些底层的内 ...

  4. 深入java虚拟机学习 -- 类的加载机制(续)

    昨晚写 深入java虚拟机学习 -- 类的加载机制 都到1点半了,由于第二天还要工作,没有将上篇文章中的demo讲解写出来,今天抽时间补上昨晚的例子讲解. 这里我先把昨天的两份代码贴过来,重新看下: ...

  5. 黑马程序猿——Java中的类载入器

    ------- android培训.java培训.期待与您交流! -------- 类载入器 Java虚拟机中能够安装多个类载入器,系统默认三个主要类载入器,每一个类负责载入特定位置的类: BootS ...

  6. java虚拟机(一)——内存管理机制与OOM异常

    一  java内存区域与内存溢出异常(OOM) 1)运行时数据区域划分        1.程序计数器(Program Conuter Register) 程序计数器是一块较小的内存空间,它是当前线程执 ...

  7. 深入java虚拟机学习 -- 类的卸载

    类的生命周期 在开始本节之前让我们再来回顾下类的生命周期 没看过前6个过程的同学建议从头看下<深入java虚拟机学习 -- 类的加载机制>,这里就不再过多介绍了,着重说下类的卸载 类的卸载 ...

  8. DexClassLoader和PathClassLoader类载入机制

    0x00 在DexClassLoader和PathClassLoader载入Dex流程一文中,我们分析了dex文件怎样形成了DexFile结构体.本文中解说类载入机制,实际上就是生成ClassObje ...

  9. 《深入理解 Java 虚拟机》学习 -- 类加载机制

    <深入理解 Java 虚拟机>学习 -- 类加载机制 1. 概述 虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的 J ...

随机推荐

  1. jquery源码ajax分析

    http://www.cnblogs.com/aaronjs/p/3683925.html

  2. Android自己定义RatingBar

    注意rating_background图片做出来的图片最好和图片四周有一定的空隙.不然会造成图片底部被拉伸的情况. <RatingBar android:layout_width="w ...

  3. UVA 1149 Bin Packing 二分+贪心

    A set of n 1-dimensional items have to be packed in identical bins. All bins have exactly the samele ...

  4. 我的Android进阶之旅------&gt;android Button上面的英文字符串自己主动大写的问题解决

    今天碰到一个关于Button的问题:android Button上面的英文字符串会自己主动变成大写,执行的Android 5.1版本号,例如以下图所看到的: 图1:Button 图2:TextView ...

  5. 关于vue 自定义组件的写法与用法

    最近在网上看到很多大神都有写博客的习惯,坚持写博客不但可以为自己的平时的学习做好记录积累 无意之中也学还能帮助到一些其他的朋友所以今天我也注册一个账号记录一下学习的点滴!当然本人能力实在有限写出的文章 ...

  6. Java-MyBatis-杂项:MyBatis根据数组、集合查询

    ylbtech-Java-MyBatis-杂项:MyBatis根据数组.集合查询 1.返回顶部 1. foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合.foreach元素的 ...

  7. struts2-action中使用通配符

    转自:https://www.cnblogs.com/ningvsban/p/3734574.html 在以前的学习中,<action>元素的配置,都是用明确的配置,其name.class ...

  8. nginx的安装步骤

    nginx学习资料;https://zhuanlan.zhihu.com/p/34943332 1.下载nginx的安装包:https://nginx.org/en/download.html 2. ...

  9. ORACLE RAC 11G 添加以及删除UNDO表空间

    在生产环境上,由于闪存盘的容量有限,现在需要将闪存盘里面的UNDO表空间,替换到非闪存的磁盘里面. 磁盘的使用情况如下: 表空间使用情况如下: RAC两个节点占用将近167G的空间. 操作步骤如下: ...

  10. C++四舍五入问题

    C++四舍五入问题: c++默认的流输出数值有效位是6,包括整数和小数,若数值超出6位,则第七位四舍五入到6位数 #include <iomanip> 输入输出库的一部分,声明了一些与提取 ...