JVM 类加载器ClassLoader源码学习笔记

类加载

 在Java代码中,类型的加载、连接与初始化过程都是在程序运行期间完成的。

类型可以是Class,Interface, 枚举等。

Java虚拟机与程序的生命周期

在如下几种情况下,Java虚拟机将结束生命周期

1)执行了System.exit() 方法

2)程序正常执行结束

3)程序在执行过程中遇到了异常或者错误而异常终止。

4) 由于操作系统出现错误导致Java虚拟机进程终止。

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

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

3、ClassLoader源码解析

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

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

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

打印parent ClassLoader

null为启动类加载器。

三、ClassLoader源码解析

ClassLoader所在的路径 

1、创建关于ClassLoader的Demo

public class MyTest15 {
    public static void main(String[] args) {
        String[] strings = new String[2];
        System.out.println(strings.getClass());
        System.out.println(strings.getClass().getClassLoader()); //启动类加载器
        System.out.println("--------------------");

        MyTest15[] myTest15s = new MyTest15[2];
        System.out.println(myTest15s.getClass().getClassLoader()); //AppClassLoader

        System.out.println("--------------------");

        int[] ints = new int[2];
        System.out.println(ints.getClass().getClassLoader());  //原生类型没有classLoader


    }
}

  打印结果:

class [Ljava.lang.String;
null
--------------------
sun.misc.Launcher$AppClassLoader@18b4aac2
--------------------
null

  

2、定位到loadClass方法

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

 3、双亲委派模型

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

findClass类的目的是自定义的ClassLoader

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

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

然后编译成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增加测试方法

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

增加包名

package com.classloader;

public class Demo {

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

}

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

 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();
    }
}

  运行结果:

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

修改如下:

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

显示结果:

Demo, MyClassLoadNameChild

原文地址:https://www.cnblogs.com/linlf03/p/10811730.html