JVM类加载机制

JVM类加载机制

当使用java命令执行某个类的main方法是,首先要使用类加载器把主类加载到JVM中;

类加载图

loadClass的大致流程

加载->验证->准备->解析->初始化->使用->卸载

加载:在硬盘中通过IO读取这个类的字节码文件;当对应的类在代码中被调用方法、创建对象等等,会加载这个类的字节码;

验证:验证读取的字节码问价是不是规范的java字节码文件;

准备:给静态变量分配内存,并赋默认值;

解析:将符号引用替换成直接引用,改阶段会把静态方法由方法名的符号引用,替换成该静态方法的所在内存的指针,这是静态链接过程动态链接是指程序运行时把符号引用替换成直接引用;

初始化:对静态变量初始化指定得值,执行静态代码块。

加载流程图

类被加载到方法区中后,主要包括:运行时常量池类型信息方法信息类加载器的引用对应class的实例引用

类加载器的引用:这个类的类加载器的实例引用;

对应class的实例引用:加载成功后会在堆中创建一个实例对象,方法区存放的是该实例对应方法的入口引用。

:jvm的加载是懒加载,只有对应的类在程序中实际使用了(只引用不行),例如new了新对象,该类才会加载。

类加载器的双亲委派机制

类加载器的种类

引导类加载器:该类加载器是由C++实现的,负责加载jvm的核心类库,位于jre文件夹下的lib中的核心类库,例如rt.jar等;

扩展类加载器:该类主要加载jvm的扩展类库,为jre/lib/ext目录下的类库;

应用程序类加载器:该类主要加载用户自己写的类,即classpath路径下的类;

类加载器的初始化

在程序运行时会创建jvm启动器的实例Launcher,该实例是单例的,保证一个虚拟机只有一个Launcher;

Launcher的构造方法中会创建两个类加载器,分别是ExtClassLoaderAppClassLoader

JVM默认使用Launcher中的getClassLoader获取AppClassLoader来作为应用程序的类加载器。

双亲委派机制

JVM类加载器亲子关系图

加载器委派流程图

首先说明父加载器不是说AppClassLoader继承了ExtClassLoader,只是在ClassLoader中有一个parent属性,AppClassLoader的parent是ExtClassLoader,ExtClassLoader的parent是null,引导类加载器是C++编写;ExtClassLoader和AppClassLoader都继承ClassLoader。

双亲委派机制是当一个类需要加载时,当前加载器先判断该类是否加载过,如果没有加载过,会委派父加载器加载该类,如果加载过,则返回该类;父加载器也会先判断是否加载过该类,如果加载过就返回该类,反之会委派上一级加载器加载;一直委派到引导类加载器,如果引导类加载器也没有加载过,则返回下一层类加载器加载,该加载器加载到,返回该类,加载不到使用下一层加载器,加载到了返回,加载不到继续下一层依次类推;

例如,加载自定义的一个类,首先通过AppClassLoader加载,如果没有加载过委托给ExtClassLoader加载,如果ExtClassLoader也没有加载过,使用引导类加载器加载,引导类加载器只加载jre的lib下的核心类库,肯定无法加载到自定义类;引导类加载器无法加载,使用ExtClassLoader类加载器加载,如果加载到,返回该类,如果加载不到,使用AppClassLoader加载。

为什么有双亲委派机制

1、沙箱安全:保证jvm的核心类库不会被篡改

2、避免类重复加载,父类加载过该类,子类又重新加载了一遍。

自定义类加载器

自定义类加载器只需要继承CLassLoader类,重新findClass方法即可;

import java.io.FileInputStream;
import java.lang.reflect.Method;

public class MyMemoClassLoader {
    static class MyClassLoader extends ClassLoader {
        private String classPath;

        public MyClassLoader(String classPath) {
            this.classPath = classPath;
        }

        private byte[] loadByte(String name) throws Exception {
            name = name.replaceAll("\\.", "/");
            FileInputStream fis = new FileInputStream(classPath + "/" + name
                    + ".class");
            int len = fis.available();
            byte[] data = new byte[len];
            fis.read(data);
            fis.close();
            return data;
        }

        protected Class<?> findClass(String name) throws ClassNotFoundException {
            try {
                byte[] data = loadByte(name);
                //defineClass将一个字节数组转为Class对象,这个字节数组是class文件读取后最终的字节数组。
                return defineClass(name, data, 0, data.length);
            } catch (Exception e) {
                e.printStackTrace();
                throw new ClassNotFoundException();
            }
        }

    }

    public static void main(String args[]) throws Exception {
        //初始化自定义类加载器,会先初始化父类ClassLoader,其中会把自定义类加载器的父加载器设置为应用程序类加载器AppClassLoader
        // 要加载的类不要放在测试项目的编译路径下,重新找个地方放,否知会使用AppClassLoader加载
        MyClassLoader classLoader = new MyClassLoader("D:/test");
        Class clazz = classLoader.loadClass("com.jvm.test.TestClassLoader");
        Object obj = clazz.newInstance();
        Method method = clazz.getDeclaredMethod("sout", null);
        method.invoke(obj, null);
        System.out.println(clazz.getClassLoader().getClass().getName());
    }
}

运行结果:
=======自己的加载器加载类调用方法=======
 com.tuling.jvm.MyMemoClassLoader$MyClassLoader
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值