【深入理解JVM】(一)类加载器双亲委派机制源码分析

概念:

主要出于安全考虑 类加载器一层层往上找,看是否被加载过,到顶后再一层层往下决定由谁去加载

一.类加载器种类

1.Bootstrap类加载器(启动类加载器)

为顶级的classloader加载路径为/lib/rt.jar charset.jar等核心类,C++实现。

2.Extension类加载器(扩展类加载器)

负责加载扩展jar包 jre/lib/ext/*.jar 或由-Djava.ext.dirs指定。

3.App类加载器(系统类加载器)

负责加载来自在命令java中的classpath或者java.class.path系统属性或者CLASSPATH操作系统属性所指定的jar类包和类路径,如果应用程序中没有自定义自己的类加载器,一般情况下这个就是程序中默认的类加载器。

4.Custom(自定义类加载器)

用户自己实现。

二.ClassLoader源码加载一个Class的过程如何体现双亲委派机制

类的加载过程是JVM将class文件从磁盘load到内存中的过程
具体步骤总结为一句话:findInCache -> parent.loadClass() -> findClass()

1.图解类加载过程

类加载过程

1.图中绿色的线代表向parent类加载器查找是否有被加载的过程
2.蓝色的线代表未加载的类应该由哪一种类加载器加载的过程

整个过程就称为双亲委派机制

2.JDK1.8源码分析类加载过程

loadClass

findLoadedClass

3.扩展:打破双亲委派机制

从上述代码中可以看出,只要重写ClassLoader的loadClass(),在loadClass()中不调用parent的loadClass(),是可以打破双亲委派机制的.
打破双亲委派机制在实际应用中,主要体现在热部署

三.自定义类加载器

搞清楚类加载过程后 可以尝试自定义一个类加载器

package com.martin.demo;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;

public class CustomClassLoader extends ClassLoader{
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        File f = new File("E:/code/", name.replace(".", "/").concat(".class"));
        try {
            FileInputStream fis = new FileInputStream(f);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int b = 0;

            while ((b=fis.read()) !=0) {
                baos.write(b);
            }

            byte[] bytes = baos.toByteArray();
            baos.close();
            fis.close();//可以写的更加严谨

            return defineClass(name, bytes, 0, bytes.length);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return super.findClass(name); //throws ClassNotFoundException
    }

    public static void main(String[] args) throws Exception {
        ClassLoader l = new CustomClassLoader();
        Class clazz = l.loadClass("com.martin.demo.A");
        Class clazz1 = l.loadClass("com.martin.demo.A");

        System.out.println(clazz == clazz1);

        A a = (A)clazz.newInstance();
        a.m();

        System.out.println(l.getClass().getClassLoader());
        System.out.println(l.getParent());

        System.out.println(getSystemClassLoader());
    }
}
package com.martin.demo;

public class A {

    public void m(){
        System.out.println("A被加载了");
    }

}

执行结果如下

true
A被加载了
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$AppClassLoader@18b4aac2

既然可以自定义类加载器,就可以将class文件加密,然后在自定义的类加载器中解密,不过实际开发中不这么干,所以就不演示了.

扩展: JDK8和JDK9中类加载器对比

由于JDK9引入的Java模块化系统(Java Platform Module System,JPMS)
为了保证兼容性,JDK 9并没有从根本上动摇从JDK 1.2以来运行了二十年之久的三层类加载器架构以及双亲委派模型。但是为了模块化系统的顺利施行,模块化下的类加载器仍然发生了一些应该被注意到变动,主要包括以下几个方面:
1.扩展类加载器(Extension Class Loader)被平台类加载器(Platform Class Loader)取代。
2.平台类加载器和应用程序类加载器都不再派生自java.net.URLClassLoader,如果有程序直接
依赖了这种继承关系,或者依赖了URLClassLoader类的特定方法,那代码很可能会在JDK 9及更高版
本的JDK中崩溃。
3.启动类加载器现在是在Java虚拟机内部和Java类库共同协作实现的类加载器,尽管有了BootClassLoader这样的Java类,但为了与之前的代码保持兼容,所有在获取启动类加载器的场景(譬如Object.class.getClassLoader())中仍然会返回null来代替,而不会得到BootClassLoader的实例。
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值