JVM(一) 类的加载过程、自定义加载器

102 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

CLASS 加载过程

image.png

Loading

首先,类加载器通过类的全路径限定名称读取类的二进制字符流(获取二进制字符流) 其次,将字节流代表的静态存储结构化为方法去的运行时数据(结构化静态数据) 最后, 在堆生成一个代表这个类的class实例(不是这个类的实例)(在内存中生成class对象)

类加载器

image.png

启动类加载器/Bootstrap ClassLoader:负责加载JAVA_HOME/lib目录中的所有类
拓展类加载器/ExtClasLoader:负责加载 JAVA_HOME/lib/ext目录中的所有类
应用程序类加载器/AppClassLoader:负责加载用户类路径ClassPath下的所有类型
双亲委派

过程:当jvm收到类的加载请求时,会首先查询上一级加载器是否加载过这个类,若加载过,直接由上一级加载器返回结果,直到最顶层的bootstrap classloader。若都没有加载过,由最顶层开始判断是否可以加载这个类,如果加载器加载目录下找不到这个类,则会交给下一级加载器去加载 我们看下源码:

image.png

image.png

首先会判断是否被当前加载器加载了,加载了则返回;如果没有则会判断当前加载器是否有上级加载器,交由上级加载器加载。 优点:可以防止类的污染,一个类只会去被加载一次。 (历史上有4次java打破了双亲委派机制的事件,感兴趣的百度看看)

自定义类加载器

优点: 可以加载指定目录下的class文件。 可以参考一下下面这篇文章 www.jianshu.com/p/7ee1bfe63… 实现: 新建一个类继承自java.lang.ClassLoader,重写它的findClass方法。 将class字节码数组转换为Class类的实例 调用loadClass方法即可

public class MyClassLoader extends ClassLoader{

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        File f = new File("C:/.../.../"+name.replaceAll("\\.","/").concat(".class"));
        try {
            FileInputStream fis = new FileInputStream(f);
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            int b = 0;
            //数据写进字节数组
            while ((b=fis.read())!=0){

                byteArrayOutputStream.write(b);
            }

            byte[] bytes = byteArrayOutputStream.toByteArray();
            byteArrayOutputStream.close();
            fis.close();
            return  defineClass("name",bytes,0,bytes.length);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return super.findClass(name);
    }
}

	ClassLoader loader = new MyClassLoader();
        Class clazz = loader.loadClass("com.....class全称");
        Object o = clazz.newInstance();

linking

Verification

验证是连接阶段的第一步,这一阶段的目的是确保Class文件的字节流中包含的信息符合《Java虚拟机规范》的全部约束要求。 验证阶段大致分为四个阶段的动作,文件格式校验,元数据验证,字节码验证,符号引用验证 借张验证阶段的图: 原文在blog.csdn.net/weixin_4389…

image.png

Preparation

准备阶段是给静态变量分配内存并 设置类变量初始值 的阶段。

Resolution

解析阶段是Java虚拟机将常量池内的符号引用替换为直接引用的过程。 符号引用(Symbolic References):符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。 直接引用(Direct References):直接引用是可以直接指向目标的指针、相对偏移量或者是一个能间接定位到目标的句柄。

initializing

这个阶段才真正开始执行java代码,静态代码块和设置变量的初始值为程序员设定的值