Java虚拟机的类载入机制
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程序的稳定执行。双亲委派模式的实现:
- 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;
- }
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的结果)。
样例代码例如以下:
- 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);
- }
- }
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虚拟机的类载入机制的更多相关文章
- JVM系列文章(四):类载入机制
作为一个程序猿,只知道怎么用是远远不够的. 起码,你须要知道为什么能够这么用.即我们所谓底层的东西. 那究竟什么是底层呢?我认为这不能一概而论.以我如今的知识水平而言:对于Web开发人员,TCP/IP ...
- 深入研究Java类载入机制
深入研究Java类载入机制 类载入是Java程序运行的第一步,研究类的载入有助于了解JVM运行过程,并指导开发人员採取更有效的措施配合程序运行. 研究类载入机制的第二个目的是让程序能动态的控制类载 ...
- 深入java虚拟机学习 -- 类的加载机制
当看到"类的加载机制",肯定很多人都在想我平时也不接触啊,工作中无非就是写代码,不会了可以百度,至于类,jvm是怎么加载的我一点也不需要关心.在我刚开始工作的时候也觉得这些底层的内 ...
- 深入java虚拟机学习 -- 类的加载机制(续)
昨晚写 深入java虚拟机学习 -- 类的加载机制 都到1点半了,由于第二天还要工作,没有将上篇文章中的demo讲解写出来,今天抽时间补上昨晚的例子讲解. 这里我先把昨天的两份代码贴过来,重新看下: ...
- 黑马程序猿——Java中的类载入器
------- android培训.java培训.期待与您交流! -------- 类载入器 Java虚拟机中能够安装多个类载入器,系统默认三个主要类载入器,每一个类负责载入特定位置的类: BootS ...
- java虚拟机(一)——内存管理机制与OOM异常
一 java内存区域与内存溢出异常(OOM) 1)运行时数据区域划分 1.程序计数器(Program Conuter Register) 程序计数器是一块较小的内存空间,它是当前线程执 ...
- 深入java虚拟机学习 -- 类的卸载
类的生命周期 在开始本节之前让我们再来回顾下类的生命周期 没看过前6个过程的同学建议从头看下<深入java虚拟机学习 -- 类的加载机制>,这里就不再过多介绍了,着重说下类的卸载 类的卸载 ...
- DexClassLoader和PathClassLoader类载入机制
0x00 在DexClassLoader和PathClassLoader载入Dex流程一文中,我们分析了dex文件怎样形成了DexFile结构体.本文中解说类载入机制,实际上就是生成ClassObje ...
- 《深入理解 Java 虚拟机》学习 -- 类加载机制
<深入理解 Java 虚拟机>学习 -- 类加载机制 1. 概述 虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的 J ...
随机推荐
- ClassNotFoundException和NoClassDefFoundError的差别
正如它们的名字所说明的:NoClassDefFoundError是一个错误(Error),而ClassNOtFoundException是一个异常,在Java中错误和异常是有差别的,我们能够从异常中恢 ...
- Qt Installer Framework的学习(三)
Qt Installer Framework的学习(三) Qt Installer Framework的样例中.通常是这种:config目录一般放了一个config.xml文件,包括的是安装配置xml ...
- 拥抱Mac之码农篇
拥抱Mac之码农篇 使用Mac大概两年时间.之前用着公司配的一台27寸的iMac.无奈机械硬盘严重拖慢速度,影响工作心情.于是入手Macbook Retina 13.这两年的开发工作所有在Mac上完毕 ...
- 光标属性CSS cursor 属性
CSS cursor 属性 CSS cursor属性,以前不知道,如果以后用到自己看的 <html> <body> <p>请把鼠标移动到单词上,可以看到鼠标指针发生 ...
- [MySQL] 查询一段时间记录
24小时内记录(即86400秒) $sql="SELECT video_id,count(id)as n FROM `rec_down` WHERE UNIX_TIMESTAMP(NOW() ...
- HIT Software Construction Lab 3
2019年春季学期 计算机学院<软件构造>课程 Lab 3实验报告 姓名 刘帅 学号 班号 1703008 电子邮件 1609192321@qq.com 手机号码 目录 1 实验目标概 ...
- ROS集成开发环境RoboWare Studio安装教程
前言:很多人说vim是最快的,所以我选择用roboware. ROS 自带的编辑器vim增加插件,效果如下: RoboWare Studio,效果如下: 下面开始安装. 一.安装 去官网 http:/ ...
- ORA-16019 和 ORA-16018 错误的处理方法(转)
一. ORA-16019 和 ORA-16018 错误产生描述 同事在修改归档目录,一不小心把参数设置错误了, 他设置的是log_archive_dest参数. 这个参数和默认log_archive_ ...
- 图片词典 Picture Dictionary
图片词典/可视词典 Picture Dictionary 某些 APP 又有新功能可以加入了.
- ASP.NET 页面缓存OutputCache用法实例(附ab压力测试对比图)
本文主要介绍Web窗体页面中的使用方法,MVC中使用方法,大家自行百度. 一.简单一行指令即可实现 <%@ OutputCache VaryByParam=" %> 这样整个页面 ...