Java类加载器@zxj
2019-04-16 17:04:30 0 举报
AI智能生成
Java类加载器@zxj
作者其他创作
大纲/内容
双亲委派模式
双亲委派模型层级(由低到高)
1.启动类加载器
2.扩展类加载器
3.应用程序类加载器
4.自定义类加载器
双亲委派模式要求
要求除了顶层的启动类加载器外,其他类加载器必须有自己的父类加载器
这里的父子关系一般不以继承的关系来实现,都使用组合关系来复用父加载器的代码
工作过程
1.当一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类
2.而是把这个请求委派给父类加载器去完成,每一层都是这样,所以加载请求最终都会传到顶层的启动类加载器中
3.只有当父类加载器无法完成加载(它的搜索范围内没有找到所需的类)时,才会交给子加载器去加载
优点
java类随着类加载器具备了带有优先级的层级关系
安全性,核心API中定义的类型不会被随意替换
例如java.lang.Object,它存放在rt.jar中,无论哪个类加载器加载该类,最终都是委派给了顶层的启动类加载器,从而保证Object类在程序的各种类加载器环境中都是同一个类,如果没有该模型限制,用户自己创建了一个java.lang.Object类,会导致程序出现多个Object,程序会变得混乱
实现原理
1.首先检查该类是否被加载过,若没有,则调用父类加载器的loadClass()
2.若父类加载器为空,则默认使用启动类加载器为父加载器
3.如果父类加载失败,则抛出ClassNotFundException异常,再调用自己的findClass()方法进行加载
破坏双亲委派模型
1.JDK1.2中开始引入双亲委派模型,为了兼容jdk1.0,在java.lang.ClassLoader中添加了一个protected方法findClass(),在此之前用户去继承ClassLoader会重写loadClass()方法,导致没有调用双亲委派模型的逻辑,后期虚拟机不建议用户再去覆盖loadClass()方法,而是把自己的类加载逻辑写到findClass()方法中
2.由于基础类又要调用回用户的代码的场景,典型的例子是JNDI服务
JNDI本身放在rt.jar中由启动类加载器加载,但是JNDI又会调用应用程序ClassPath中的代码,但启动类加载器不可能认识这些代码,所以引入一个设计:线程上下文类加载器,JNDI服务使用该加载器去加载所需要的代码,也就是父类加载器去请求子类加载器去完成类加载的动作,违反了双亲委派模型的层次结构来逆向使用类加载器
3.OSGi实现模块化热部署
在OSGi环境下,它自定义的类加载器机制的实现,不再是双亲委派模型的树状结构,而是更为复杂的网状
类与类加载器
两个Class是否是同一个类对象
包名必须一致
加载这个类的ClassLoader必须一致
类加载过程
加载
1.通过一个类的全限定名来获取定义此类的二进制字节流
二进制字节流的获取方式
1.从zip包中读取,例如jar,war等
2.从网络中获取,例如Applet
3.运行时计算生成,例如动态代理技术
4.由其他文件生成,例如JSP
5.从数据库读取,例如有些中间件服务器(SAP Netweaver)
2.将字节流代表的静态存储结构转换为方法区的运行时数据结构
3.在方法区内存中生成代表此类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
验证
1.文件格式验证
验证字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理
Class文件中各个部分是否有被删除或附加的其他信息等
2.元数据验证
对类的元数据信息进行语义校验,例如是否有父类(除了Object类都应有父类)
是否继承了不允许被继承的final类等
3.字节码验证
确定语义是合法的,符合逻辑的,对类的方法体进行校验,保证方法在运行时不会危害虚拟机安全
4.符号引用验证
对类自身以外的(常量池中的各种符号引用)进行匹配性校验,例如:符号引用中通过字符串描述的全限定名是否能找到对应的类
也可以使用-Xverify:none参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间
准备
准备阶段是正式为类变量(这里指静态变量,不包过实例变量,实例变量会在对象实例化时和对象一起分配在java heap中)分配内存并设置类初始值(通常都为0)的阶段,这些变量使用的内存都在方法区中进行分配
解析
1.类和接口的解析
2.字段解析
3.类方法解析
4.接口方法解析
初始化
初始化类变量和其他资源
四种类加载器
启动类加载器(Bootstrap ClassLoader)
C++语言实现,是虚拟机自身的一部分,其他类加载器都是由Java语言实现,独立于虚拟机外部
负责将存放在<JAVA_HOME>\lib目录中,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的类库加载到虚拟机内存
按照文件识别加载jar包,如rt.jar,名字不符合的类库即使放在lib目录中也不会被加载
rt.jar中常用的类库有java.lang.xxx类库,java.util.xxx类库,java.io.xxx类库等
启动类加载器无法被Java程序直接引用
扩展类加载器(Extension ClassLoader)
由sun.misc.Launcher$ExtClassLoader实现
负责加载<JAVA_HOME>\lib\ext目录中的或者被java.ext.dirs系统变量所指定的路径中的所有类库
扩展类加载器可以被开发者直接使用
应用程序类加载器(Application ClassLoader)
由sun.misc.Launcher$AppClassLoader实现
负载加载用户类路径(ClassPath)上所指定的类库
程序中默认的类加载器,也称为系统类加载器
通过ClassLoader.getSystemClassLoader()可以获取
自定义类加载器
父类是AppClassLoader
一般继承URLClassLoader,不用自己写findClass()方法
显示加载和隐式加载
显示加载:在代码中通过调用ClassLoader加载class对象
Class.forName(name)
this.getClass().getClassLoader().loadClass()
隐式加载:虚拟机自动加class对象加载到内存中
在加载某个类的class文件时,该类的class文件中引用了另一个类的对象,此时被引用的类将通过JVM自动加载到内存中
收藏
收藏
0 条评论
下一页