JVM类加载器篇(下)
Posted ProChick
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM类加载器篇(下)相关的知识,希望对你有一定的参考价值。
1.虚拟机自带的类加载器
- 启动类加载器(也叫作引导类加载器,BootStrap ClassLoader)
- 这个类加载加载器使用C/C++语言实现的,嵌套在JVM内部
- 它用来加载java的核心库( JAVA_HOME/jre/lib/rt.jar/resources.jar或sun.boot.class.path路径下的内容 ),用于提供JVM自身需要的类,处于安全考虑,它只加载包名为java、javax、sun等开头的类
- 本身并不继承自java.lang.ClassLoader,没有父加载器
- 用于加载拓展类和应用程序类加载器,并指定为他们的父加载器
- 拓展类加载器(Extension ClassLoader)
- java语言编写,由sun.misc.Launcher$ExtClassLoader实现,派生于ClassLoader类
- 父类加载器为启动类加载器
- 从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的jre/lib/ext子目录下加载类库。所以如果用户创建的JAR放在此目录下,也会由拓展类加载器进行加载。
- 应用程序类加载器(也叫作系统类加载器,AppClassLoader)
- java语言编写, 由sun.misc.Launcher$AppClassLoader实现,派生于ClassLoader类
- 父类加载器为拓展类加载器
- 它负责加载环境变量classpath或系统属性 java.class.path指定路径下的类库
- 它是程序中默认的类加载器,一般来说,java应用编写的类都是由它来完成加载
代码举例
public static void main(String[] args) {
System.out.println("********启动类加载器能够加载的路径*********");
URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (URL e:urls){
System.out.println(e.toExternalForm());
}
System.out.println("********拓展类加载器能够加载的路径********");
String extDirs = System.getProperty("java.ext.dirs");
for (String path : extDirs.split(";")){
System.out.println(path);
}
System.out.println("********系统类加载器能够加载的路径为所有自定义类路径********");
}
2.用户自定义的类加载器
-
可以有哪些用途
- 隔离加载类
- 修改类加载的方式
- 拓展加载源
- 防止源码泄漏
-
一般的实现方法
- 开发人员可以通过继承抽象类
java.lang. ClassLoader
类的方式,实现自己的类加载器,以满足一些特殊的需求 - 在JDK1.2之前,在自定义类加载器时,总会去继承
classLoader
类并重写loadclass()
方法,从而实现自定义的类加载类,但是在JDK1.2之后已不再建议用户去覆盖loadclass()
法,而是建议把自定义的类加载逻辑写在findclass()
方法中 - 在编写自定义类加载器时,如果没有太过于复杂的需求,可以直接继承
URLClassLoader
类,这样就可以避免自己去编写findclass()
方法及其获取字节码流的方式,使自定义类加载器编写更加简洁。
- 开发人员可以通过继承抽象类
3.类加载器的常见方法
-
ClassLoader类中的方法
-
ClassLoader获取的方法
4.类加载器的双亲委派机制
-
什么是双亲委派机制?
Java虚拟机对class文件采用的是按需加载的方式,也就是说当需要使用该类时才会将它的class文件加载到内存生成的class对象。而且加载某个类的class文件时,java虚拟机采用的是双亲委派模式,即把请求交由父类处理,它是一种任务委派模式。
-
基本原理
举例说明
从上图可以看出,虽然我们自定义了一个java.lang
包下的String
尝试覆盖核心类库中的String类,但是由于双亲委派机制,启动加载器会加载java核心类库的String类(BootStrap启动类加载器只加载包名为java、javax、sun等开头的类),而核心类库中的String并没有main方法 -
特点优点
-
避免类的重复加载
:一个类只会被一个类加载器加载 -
保护程序安全,防止核心API被随意篡改
: 阻止我们用核心包名自定义类
-
5.类加载器的引用
-
什么是类加载器的引用?
JVM必须知道一个类型是由启动类加载器加载的还是由用户类加载器加载的。如果一个类型由用户类加载器加载的,那么JVM会将这个类加载器的一个引用作为类型信息的会议部分保存在方法区中。当解析一个类型到另一个类型的引用的时候,JVM需要保证两个类型的加载器是相同的。
注意
:在JVM中表示两个class对象是否为同一个类,存在的两个必要条件- 类的完整类名必须一致,包括包名
- 加载这个类的ClassLoader(指ClassLoader实例对象)必须相同
-
主动引用的场景
- 创建类的实例
- 访问某各类或接口的静态变量,或者对静态变量赋值
- 调用类的静态方法
- 反射,比如Class.forName(com.dsh.jvm.xxx)
- 初始化一个类的子类
- java虚拟机启动时被标明为启动类的类
- JDK 7 开始提供的动态语言支持:java.lang.invoke.MethodHandle实例的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic句柄对应的类没有初始化,则初始化
-
被动引用的场景
除了以上七种情况,其他使用java类的方式都被看作是对类的被动使用,都不会导致类的初始化
以上是关于JVM类加载器篇(下)的主要内容,如果未能解决你的问题,请参考以下文章