zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    WIN8 下 Hyper-V和Vmware Workstation
    小技巧总结
    工具软件
    php开发入门
    docker的用法总结
    [工具] 同步本地文件夹与VPS中的文件夹
    读书笔记之《The Art of Readable Code》Part 3
    读书笔记之《The Art of Readable Code》Part 2
    正则表达式小试牛刀
    读书笔记之《The Art of Readable Code》part 1
  • 原文地址:https://www.cnblogs.com/linlf03/p/10811730.html
Copyright © 2011-2022 走看看