1.JVM运行流程,JVM基本结构

2、类加载器双亲委派模型

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

3、ClassLoader源码解析

4、从源码分析实现自定义类加载器

 

一、JVM运行流程,JVM基本结构

JVM ClassLoader源码学习笔记 随笔 第1张

 

JVM基本结构

类加载器,运行时数据区,执行引擎,本地接口

Class Files -> ClassLoader -> 运行时数据区 -> 执行引擎 -> 本地方法库

 

类的装载:

   加载,连接(验证,准备,解析),初始化,使用,卸载

 

初始化: 执行类的构造器<clinit>,为类的静态变量赋初始值

构造器:

  1、static变量

        2、staitc{} 语句

构造方法: 实例化的对象

 

二、类加载器双亲委派模型

1、为什么使用双亲委派模型

    避免重复加载

 

2、JDK已有的类加载器

BootStrap ClassLoader JVM自己的类加载器,启动加载器。(C++) 主要加载rt.jar

Extension ClassLoader extends ClassLoader 扩展类加载器  加载%Java_home%lib/ext/*.jar

APP ClassLoader extends ClassLoader 应用加载器  -> Classpath

 

打印Class Loader

JVM ClassLoader源码学习笔记 随笔 第2张

 

打印parent ClassLoader

JVM ClassLoader源码学习笔记 随笔 第3张

null为启动类加载器。

 

ClassLoader所在的路径 

JVM ClassLoader源码学习笔记 随笔 第4张

 

定位到loadClass方法

JVM ClassLoader源码学习笔记 随笔 第5张

 

loadClass(name,false)方法

    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 {
                    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;
        }
    }

  双亲委派模型

JVM ClassLoader源码学习笔记 随笔 第6张

如果parent不为空,则调用parent的loadClass方法。

 

findClass类的目的是自定义的ClassLoader

JVM ClassLoader源码学习笔记 随笔 第7张

 

3、自定义类加载器 extends ClassLoader  -> 完成自定义加载路径

1) 创建Demo.java文件,路径为D:/tmp/Demo.java

JVM ClassLoader源码学习笔记 随笔 第8张

然后编译成class文件

javac Demo.java

 

2) 创建自定义类加载器

package com.classloader;


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

public class MyClassLoader extends ClassLoader {

    private String path; //加载的路径
    private  String name; //类加载器名称

    public MyClassLoader(String name, String path){
        super(); //让系统类加载器成为该类的父加载器
        this.name = name;
        this.path = path;
    }

    public MyClassLoader(ClassLoader parent, String name, String path){
        super(parent); //显示指定父加载器
        this.name = name;
        this.path = path;
    }

    /**
     * 加载自定义的类,通过自定义的ClassLoader
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
       byte[] data = readClassFileToByteArray(name);
        return this.defineClass(name, data,0, data.length);
    }

    /**
     * 获取.class文件的字节数组
     * com.classLoader.Demo ->
     * D:/temp/com/classLoader/Demo.class
     * @return
     */
    private byte[] readClassFileToByteArray(String name) {
        InputStream is = null;
        byte[] returnData = null;

        name = name.replaceAll("\\.","/");
        String filePath = this.path + name + ".class";
        File file = new File(filePath);

        ByteArrayOutputStream os = new ByteArrayOutputStream();
        try{
            is = new FileInputStream(file);
            int tmp = 0;
            while ((tmp = is.read()) != -1){
                os.write(tmp);
            }
            returnData = os.toByteArray();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                if(is != null){
                    is.close();
                }

                if(os != null){
                    os.close();
                }

            }catch (Exception e2){

            }
        }

        return  returnData;

    }

    @Override
    public String toString() {
        return  this.name;
    }
}

  

 

 

3、使用自定义类加载器

public class TestDemo {

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        MyClassLoader loader = new MyClassLoader("MyClassLoadName1", "D:/tmp/");
        Class<?> c = loader.loadClass("Demo");
        c.newInstance();
    }
}

  运行结果

    Demo, MyClassLoadName1

 

4、测试父加载器

1)、工程里的Demo.java增加测试方法

JVM ClassLoader源码学习笔记 随笔 第9张

 

2)、修改D盘下的Demo.java

增加包名

package com.classloader;

public class Demo {

    public Demo(){
        System.out.println("Demo, "  + this.getClass().getClassLoader());
    }

}

  路径为,并且重新生成class文件

JVM ClassLoader源码学习笔记 随笔 第10张

 

 3) 测试文件

public class TestDemo {

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        MyClassLoader loader = new MyClassLoader("MyClassLoadName1", "D:/tmp/");
        Class<?> c = loader.loadClass("com.classloader.Demo");
        c.newInstance();
    }
}

  运行结果:

JVM ClassLoader源码学习笔记 随笔 第11张

 说明使用的是父类加载器,加载的是工程里的那个Demo.class文件。

 

修改如下:

JVM ClassLoader源码学习笔记 随笔 第12张

传入null,说明父加载器为启动加载器。

显示结果:

Demo, MyClassLoadNameChild

 

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄