前文如上:
39.【面试宝典】面试宝典-redis过期k值回收策略,缓存淘汰策略
合集参考:面试宝典
文档参考:《深入理解Java虚拟机 JVM高级特性与最佳实践》第3版_周志明
类加载器
虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到 Java 虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称为“类加载器”
1.类与类加载器
2.类加载器
我们看一下JVM预定义的三种类加载器,当JVM启动的时候,Java缺省开始使用如下三种类型的类加载器:
启动(Bootstrap)类加载器: 引导类加载器是用 本地代码实现的类加载器,它负责将 /lib下面的核心类库 或 -Xbootclasspath选项指定的jar包等虚拟机识别的类库 加载到内存中。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以 不允许直接通过引用进行操作。
扩展(Extension)类加载器: 扩展类加载器是由Sun的ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现的,它负责将 /lib/ext或者由系统变量-Djava.ext.dir指定位置中的类库 加载到内存中。开发者可以直接使用标准扩展类加载器。
系统(System)类加载器: 系统类加载器是由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的,它负责将 用户类路径(java -classpath或-Djava.class.path变量所指的目录,即当前类所在路径及其引用的第三方类库的路径)下的类库 加载到内存中。开发者可以直接使用系统类加载器。
自定义类加载器:开发人员可以通过继承 java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求。一般来说,自己开发的类加载器只需要覆写 findClass(String name)方法即可。java.lang.ClassLoader类的方法loadClass()封装了前面提到的代理模式的实现。该方法会首先调用findLoadedClass()方法来检查该类是否已经被加载过;如果没有加载过的话,会调用父类加载器的loadClass()方法来尝试加载该类;如果父类加载器无法加载该类的话,就调用findClass()方法来查找该类。因此,为了保证类加载器都正确实现代理模式,在开发自己的类加载器时,最好不要覆写 loadClass()方法,而是覆写 findClass()方法。
3.java.class.ClassLoader类
(1)作用:
- java.lang.ClassLoader类的基本职责就是根据一个指定的类的名称,找到或者生成其对应的字节代码,然后从这些字节代码中定义出一个Java类,即java.lang.Class类的一个实例。
- ClassLoader还负责加载 Java 应用所需的资源,如图像文件和配置文件等。
(2)常用方法:
- getParent() 返回该类加载器的父类加载器。
- loadClass(String name) 加载名称为 name的类,返回的结果是java.lang.Class类的实例。 此方法负责加载指定名字的类,首先会从已加载的类中去寻找,如果没有找到;从parent ClassLoader[ExtClassLoader]中加载;如果没有加载到,则从Bootstrap ClassLoader中尝试加载(findBootstrapClassOrNull方法), 如果还是加载失败,则自己加载。如果还不能加载,则抛出异常ClassNotFoundException。
- findClass(String name) 查找名称为 name的类,返回的结果是java.lang.Class类的实例。
- findLoadedClass(String name) 查找名称为 name的已经被加载过的类,返回的结果是 java.lang.Class类的实例。
- defineClass(String name, byte[] b, int off, int len) 把字节数组 b中的内容转换成 Java 类,返回的结果是java.lang.Class类的实例。这个方法被声明为 final的。
- resolveClass(Class<?> c) 链接指定的 Java 类。
4.双亲委派机制
5.为什么使用双亲委派
使用双亲委派模型来组织类加载器之间的关系,有一个显而易见的好处就是 Java 类随 着它的类加载器一起具备了一种带有优先级的层次关系。例如类 java.lang.Object ,它存放在 rt.jar 之中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此 Object 类在程序的各种类加载器环境中都是同一个类。
相反,如果没有使用双亲委派模型,由各个类加载器自行去加载的话,如果用户自己编写了一个称为 java.lang.Object 的类,并放在程序的 ClassPath 中,那系统中将会出现多个不同的 Object 类, Java 类型体系中最基础的行为也就无法保证,应用程序也将会变得一片棍混乱。如果感兴趣的话,可以尝试去编写一个与 rt.jar 类库中已有类重名 Java 类, 将会发现可以正常编译,但永远无法被加载运行
双亲委派模型对于保证 Java 程序的稳定运作很重要 ,但它的实现却非常简单,实现双 委派的代码都集中在 java.lang. ClassLoader loadClass ()方法之中,如下图所示,逻辑清晰易懂:先检查是否已经被加载过,若没有加载则调用父加载器的 loadClass() 方法,若父加载器为空则默认使用启动类加载器作为父加载器,如果父类加载失败,抛出 Class Not FoundException 异常后,再调用自己的 findClass ()方法进行加载。
公众号,感谢关注