类加载和双亲委派机制
类加载是什么?
虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型
类加载的时机?
主动引用例子:
类的生命周期?
加载 loading
>>>>连接(验证 verification 准备 preparation 解析 resolution)
>>>>初始化 initialization
>>>>使用 using
>>>>卸载 unloading
类加载过程
加载
1.从Class文件获取二进制字节流
2.将字节流中的静态结构转化为方法区的运行时的动态结构
3.在内存中生成代表该Class的java.lang.Class对象,作为方法区该类的访问入口。
连接
验证:验证Class文件的字节流中包含的信息是否符合JVM的要求,并确保不会危害JVM自身的安全。
准备:为静态变量分配内存并赋初始值
解析:将常量池内的符号引用转换为直接引用
初始化
ClassLoader是什么?
它负责将 Class 的字节码形式(本质就是一个字节数组 byte[])转换成内存形式的 Class 对象
字节码有特定的复杂的内部格式,可以来自于磁盘文件 *.class,也可以是 jar 包里的 *.class,也可以来自远程服务器提供的字节流
很多字节码加密技术就是依靠定制 ClassLoader 来实现的。先使用工具对字节码文件进行加密,运行时使用定制的 ClassLoader 先解密文件内容再加载这些解密后的字节码
每个 Class 对象的内部都有一个 classLoader 字段来标识自己是由哪个 ClassLoader 加载的
class Class<T> { ... private final ClassLoader classLoader; ... }
延迟加载
JVM 运行并不是一次性加载所需要的全部类,而是按需加载,也就是延迟加载
程序在运行的过程中会逐渐遇到很多不认识的新类,这时候就会调用 ClassLoader 来加载这些类
加载完成后就会将 Class 对象存在 ClassLoader 里面,下次就不需要重新加载了
JVM有哪些类加载器?
1、启动类加载器 BootstrapClassLoader
负责加载 JVM 运行时核心类,这些类位于 JAVA_HOME/lib/rt.jar 文件中,常用内置库 java.xxx.* 都在里面,比如 java.util.*、java.io.*、java.nio.*、java.lang.* 等等
这个 ClassLoader 比较特殊,由 C 代码实现,称之为「根加载器」
2、扩展类加载器 ExtensionClassLoader
负责加载 JVM 扩展类,它们的 jar 包位于 JAVA_HOME/lib/ext/*.jar 中,如 swing 系列、内置的 js 引擎、xml 解析器 等等,这些库名通常以 javax 开头
3、应用程序类加载器 ApplicationClassloader(系统类加载器)
直接面向用户的加载器,加载 Classpath 环境变量里定义的路径中的 jar 包和目录。我们自己编写的代码以及使用的第三方 jar 包通常都是由它来加载
使用ClassLoader类的getSystemClassLoader()方法即可获取这个加载器
双亲委派机制
当一个类加载器收到一个类加载请求时(加载.class文件),首先会把这个任务委托给父类加载器,每个类加载器递归这个操作,
只有在父类加载器在自己的搜索范围内找不到所需指定类时,子类加载器才会尝试自己去加载
俗称:"儿子"先不干,先让"老子"干,"老子"再让"老子"干....,直到干不了,再依次返回让"儿子"干
参看ClassLoader源码
class ClassLoader { ... private final ClassLoader parent; ... }
工作过程:
1.当Application ClassLoader 收到一个类加载请求时,他首先不会自己去尝试加载这个类,而是将这个请求委派给父类加载器Extension ClassLoader去完成。
2.当Extension ClassLoader收到一个类加载请求时,他首先也不会自己去尝试加载这个类,而是将请求委派给父类加载器Bootstrap ClassLoader去完成。
3.如果Bootstrap ClassLoader加载失败(在<JAVA_HOME>\lib中未找到所需类),就会让Extension ClassLoader尝试加载。
4.如果Extension ClassLoader也加载失败,就会使用Application ClassLoader加载。
5.如果Application ClassLoader也加载失败,就会使用自定义加载器去尝试加载。
6.如果均加载失败,就会抛出ClassNotFoundException异常。
为什么要用双亲委派机制?
.class
。通过委托去向上面问一问,加载过了,就不用再加载一遍2、保证核心
.class
不能被篡改。通过委托方式,不会去篡改核心.class
,即使篡改也不会去加载,即使加载也不会是同一个.class
对象了.class
也不是同一个Class
对象。这样保证了Class
执行安全。