虚拟机类加载子系统(理论到实践)

虚拟机类加载子系统

  • 类加载过程
    • 加载
    • 验证
    • 准备
    • 解析
    • 初始化
  • 双亲委派模型
    • 类加载器的类别
    • 类加载器双亲委派模型
    • 工作过程
      • 源码说明
    • 作用
  • 自定义类加载器

类加载过程

加载 —》连接 —》初始化 —》使用 —》卸载
连接:验证 —》准备 —》解析

加载

通过一个类的全限定名来获取定义此类的二进制字节流。
将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。

验证

文件格式验证
元数据验证
字节码验证
符号引用验证

准备

准备阶段是正式为类中定义的变量(即静态变量,被static修饰的变量)分配内存并设置类变量初始值的阶段
关于准备阶段,还有两个容易产生混淆的概念笔者需要着重强调,首先是这时候进行内存分配的仅包括类变量,而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在Java堆中。其次是这里所说的初始值“通常情况”下是数据类型的零值

解析

解析阶段是Java虚拟机将常量池内的符号引用替换为直接引用的过程

初始化

初始化阶段就是执行类构造器()方法的过程

双亲委派模型

类加载器的类别

1.BootstrapClassLoader(启动类加载器)
c++编写,加载java核心库 java.*,构造ExtClassLoader和AppClassLoader。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作
2.ExtClassLoader (标准扩展类加载器)
java编写,加载扩展库,如classpath中的jre ,javax.*或者
java.ext.dir 指定位置中的类,开发者可以直接使用标准扩展类加载器。
3.AppClassLoader(系统类加载器)
java编写,加载程序所在的目录,如user.dir所在的位置的class

类加载器双亲委派模型

虚拟机类加载子系统(理论到实践)_第1张图片

自定义类加载器-》应用程序类加载器(系统类加载器)-》扩展类加载器-》启动类加载器

工作过程

如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载器请求最终都应该传送到最顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求时,子加载器才会尝试去完成加载

源码说明

    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
     
        synchronized (getClassLoadingLock(name)) {
     
            // First, check if the class has already been loaded
            // 先判断类有木有被加载
            Class<?> c = findLoadedClass(name);
            if (c == null) {
     
                long t0 = System.nanoTime();
                try {
     
                //如果有父类就用父类的类加载器,知道父类未null(即启动类加载器)
                    if (parent != null) {
     
                        c = parent.loadClass(name, false);
                    } else {
     
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
     
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
     
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    //如果父类失败则用子类加载
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
     
                resolveClass(c);
            }
            return c;
        }
    }

作用

1、防止重复加载同一个.class。通过委托去向上面问一问,加载过了,就不用再加载一遍。保证数据安全。
2、保证核心.class不能被篡改。通过委托方式,不会去篡改核心.clas,即使篡改也不会去加载,即使加载也不会是同一个.class对象了。不同的加载器加载同一个.class也不是同一个Class对象。这样保证了Class执行安全。

自定义类加载器

自定义加载器就是继承ClassLoader实现findClass方法

/**
 * 自定义类加载器
 */
public class MyClassLoader extends ClassLoader{
     

    public static void main(String[] args) throws ClassNotFoundException {
     
        System.out.println(MyClassLoader.class.getClassLoader());
        System.out.println(MyClassLoader.class.getClassLoader().getParent());
        System.out.println(MyClassLoader.class.getClassLoader().getParent().getParent());
        MyClassLoader myClassLoader = new MyClassLoader();
        Class<?> c = myClassLoader.findClass("com.example.demo.jvm.GC");
        System.out.println(c.getClassLoader());
    }

    protected Class<?> findClass(String name) throws ClassNotFoundException
    {
     
        byte[] data = loadClassData(name);
        return defineClass(name, data, 0, data.length);
    }

    private byte[] loadClassData(String url){
     
        File file = new File("D:/company/GC.class");
        try {
     
            FileInputStream fis = new FileInputStream(file);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] b = new byte[1024];
            int len;
            while((len = fis.read(b)) != -1) {
     
                bos.write(b, 0, len);
            }
            return bos.toByteArray();
        } catch (IOException e) {
     
            e.printStackTrace();
        }
        return new byte[0];
    }
}

执行结果:看出来MyClassLoader 的类加载器是AppClassLoader,父类是ExtClassLoader ,最终父类是BootstrapClassLoader,应该BootstrapClassLoader是C写的,所以我们这里打印出来为空
虚拟机类加载子系统(理论到实践)_第2张图片

你可能感兴趣的:(JVM系列,jvm,java)