【JVM】之类加载机制

1、类加载简介java

        类加载机制:JVM把class文件加载到内存,并对数据进行校验、解析和初始化,最终造成JVM能够直接使用的Java类型的过程。apache

2、bootstrap

        加载:将class文件字节码内容经过类加载器加载到内存中,并将这些静态数据转换成方法区中的运行时数据结构,在堆中生成一个表明这个类的java.lang.Class对象,做为方法区类数据的访问入口(外部能够经过Class对象,做为操做类的入口)。这个过程须要类加载器参与。数组

        连接:将Java类的二进制代码合并到JVM的运行状态之中的过程。【验证:确保加载的类信息符合JVM规范,没有安全方面的问题。准备:正式为类变量(static变量)分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配。解析虚拟机常量池内的符号引用替换为直接引用的过程。】缓存

        初始化:1.初始化阶段是执行类构造器<clinit>()方法的过程。类构造器<clinit>()方法是由编译器自动收集类中的全部类变量的赋值动做静态语句块(static块)中的语句合并产生的。 2.当初始化一个类的时候,若是发现其父类尚未进行过初始化,则须要先触发其父类的初始化。 3.虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步。当访问一个Java类的静态域时,只有真正声明这个域的类才会被初始化。tomcat

        类的主动引用(必定会发生类的初始化)安全

        - new 一个类的对象服务器

        - 调用类的静态成员(除了final常量)和静态方法网络

        - 使用java.lang.reflect包的方法对类进行反射调用数据结构

        - 当虚拟机启动,java Hello,则必定会初始化Hello类,说白了就是先启动main方法所在的类

        - 当初始化一个类,若是其父类没有被初始化,则先会初始化他的父类

        类的被动引用(不会发生类的初始化)

        - 当访问一个静态域时,只有真正声明这个域的类才会被初始化

            . 经过子类引用父类的静态变量,不会致使子类初始化

        - 经过数组定义类引用,不会触发此类的初始化

        - 引用常量不会触发此类的初始化(常量在编译阶段就存入调用类的常量池中了)

3、

public class Demo01 {
    public static void main(String[] args) {
        System.out.println("初始化Demo01");
        //A a = new A();
       // System.out.println("width= " + A.width);
       // System.out.println(A.MAX);
       // System.out.println(Child_A.width);
        A[] arr = new A[]{};
    }
}
class A extends Father_A {
    static {
        System.out.println("静态初始化块");
        width = 200;
    }
    public static int width = 100;
    public static final int MAX = 100;
    public A() {
        System.out.println("构造函数初始化中");
    }
    {
        System.out.println("初始化块");
        width = 300;
    }
}
class Father_A {
    static {
        System.out.println("静态初始化块——Father_A");
    }
}

class Child_A extends A {
    static {
        System.out.println("静态初始化Child_A");
    }
}

4、深刻类加载器

        类加载器的做用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区中的运行时数据结构,在堆中生成一个表明这个类的java.lang.Class对象,做为方法区类数据的访问入口。

        类缓存:标准的JavaSE类加载器能够按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过,JVM垃圾收集器能够回收这些Class对象。

    类加载器的层次结构(树状结构)

        引导类加载器(bootstrap class loader)

        - 它用来加载Java的核心库(JAVA_HOME/jre/lib/rt.jar,或sum.boot.class.path路径下的内容),是用原生代码来实现的,并不继承自java.lang.ClassLoader。

        - 加载扩展类和应用程序类加载器,并指定他们的父类加载器。

        扩展类加载器(extensions class loader)

        - 用来加载Java的扩展库(JAVA_HOME/jre/ext/*.jar,或java.ext.dirs路径下的内容)。Java虚拟机的实现会提供一个扩展库目录。该类加载器在此目录路面查找并加载Java类。

        - 由sum.misc.Launcher$ExtClassLoader实现

        应用程序类加载器(application class loader)

        - 它根据Java应用的类路径(classpath,java.class.path路径)。通常来讲,Java应用的类都是由它来完成加载的。

        - 由sun.misc.Launcher$AppClassLoader实现。

        自定义类加载器

            自定义类加载器的流程

            - 继承:java.lang.ClassLoader

            - 首先检查请求的类型是否已经被这个类装载器装载到命名空间中了,若是已经装载,直接返回;

            - 委派类加载请求给父类加载器,若是父类加载器可以完成,则返回父类加载器加载的Class实例;

            - 调用本类加载器的findClass(...)方法,试图获取对应的字节码,若是获取的到,则调用defineClass(...)导入类型到方法区;若是获取不到对应的字节码或者其它缘由失败,返回异常给loadClass(...),loadClass(...)转抛异常,终止加载过程;

            - 注意:被两个类加载器加载的同一个类,JVM不认为是相同的类。

        文件类加载器

import org.apache.commons.io.IOUtils;

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

public class FileSystemClassLoader extends ClassLoader {

    private String rootDir;

    public FileSystemClassLoader(String rootDir) {
        this.rootDir = rootDir;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class<?> clazz = findLoadedClass(name);
        /**
         * 先查询有没有加载过这个类,若是已经加载,则直接返回加载好的类;若是没有,则加载新的类
         */
        if (clazz != null) {
            return clazz;
        } else {
            /**
             * 委派类加载请求给父类加载器
             */
            ClassLoader parent = this.getParent();
            try {
                clazz = parent.loadClass(name);
            } catch (Exception e) {

            }
            if (clazz != null) {
                return clazz;
            } else {
                byte[] classData = getClassData(name);
                if (classData == null) {
                    throw new ClassNotFoundException();
                } else {
                    clazz = defineClass(name, classData,0, classData.length);
                }
            }
        }
        return clazz;
    }

    private byte[] getClassData(String classname) {
        String path = rootDir + "/" + classname.replace(".", "/") + ".class";
        /**
         * 注:IOUtils,可使用它将流中的数组转换成字节数组
         */
        InputStream is = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            is = new FileInputStream(path);
            byte[] buffer = new byte[1024];
            int temp = 0;
            while ((temp = is.read(buffer)) != -1) {
                baos.write(buffer, 0, temp);
            }
            return baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
                if (baos != null) {
                    baos.close();
                }
            }catch (IOException e) {
                e.printStackTrace();
            }
        }
      /*  InputStream is = null;
        try {
            is = new FileInputStream(path);
            byte[] bytes = IOUtils.toByteArray(is);
            return bytes;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }*/
    }
}
ipublic class Demo02 {
    public static void main(String[] args) throws ClassNotFoundException {
        FileSystemClassLoader classLoader = new FileSystemClassLoader("/Users/Work/mimidai/mimidai-test");
        FileSystemClassLoader classLoader2 = new FileSystemClassLoader("/Users/Work/mimidai/mimidai-test");

        Class<?> clazz = classLoader.loadClass("HelloWordI");
        Class<?> clazz1 = classLoader.loadClass("HelloWordI");
        Class<?> clazz2 = classLoader2.loadClass("HelloWordI");
        Class<?> clazz3 = classLoader2.loadClass("java.lang.String");
        Class<?> clazz4 = classLoader2.loadClass("com.mimidai.test2.Demo01");

        System.out.println(clazz.hashCode());
        System.out.println(clazz1.hashCode());
        System.out.println(clazz2.hashCode());
        System.out.println(clazz.getClassLoader());
        System.out.println(clazz2.getClassLoader());
        System.out.println(clazz3.getClassLoader());
        System.out.println(clazz4.getClassLoader());
    }
}

输出结果

363771819
363771819
2065951873
com.mimidai.test2.FileSystemClassLoader@16b98e56
com.mimidai.test2.FileSystemClassLoader@27d6c5e0
null
sun.misc.Launcher$AppClassLoader@18b4aac2

        网络类加载器

import org.apache.commons.io.IOUtils;
import java.io.InputStream;
import java.net.URL;

public class NetClassLoader extends ClassLoader {

    private String rootURL;

    public NetClassLoader(String rootURL) {
        this.rootURL = rootURL;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class<?> clazz = this.findLoadedClass(name);

        if (clazz != null) {
            return clazz;
        } else {
            ClassLoader parentClassLoader = this.getParent();
            try {
                clazz = parentClassLoader.loadClass(name);
            } catch (Exception e) {

            }
            if (clazz != null) {
                return clazz;
            } else {
                byte[] classData = getClassData(name);
                if (classData != null) {
                    clazz = defineClass(name, classData,0, classData.length);
                    return clazz;
                } else {
                    throw new ClassNotFoundException();
                }
            }
        }
    }

    private byte[] getClassData(String name) {
        String path = rootURL + name.replace(".", "/");

        InputStream is = null;
        try {
            URL url = new URL(path);
            is = url.openStream();
            byte[] clazzDate = IOUtils.toByteArray(is);
            return clazzDate;
        } catch (Exception e) {
            try {
                if (is != null) {
                    is.close();
                }
            } catch (Exception e1) {

            }
            return null;
        }
    }
}

        加密解密类加载器(取反操做,DES对称加密解密)

5、java.lang.ClassLoader类介绍

    - java.lang.ClassLoader类的基本职责就是根据一个指定的类的名称,找到或者生成器对应的字节代码,而后从这些字节代码中定义出一个Java类,即java.lang.Class类的一个实例。

    - 除此以外,ClassLoader还负责加载Java应用所需的资源,如图像文件和配置文件等。

    - 相关方法

        - getParent() 返回该类加载器的父类加载器。

        - loadClass(String name) 加载名称为name的类,返回的结果是java.lang.Class类的实例。

           . 此方法负责加载指定名字的类,首先会从已加载的类中去寻找,若是没有找到;从parent ClassLoader[ExtClassLoader]中加载;若是没有加载到,则从Bootstrap ClassLoader中尝试加载(findBootstrapClassOrNull方法),若是仍是加载失败,则本身加载。若是还不能加载,则抛出异常ClassNotFoundException。

            . 若是要改变类的加载顺序能够覆盖此方法;

        - findClass(String name) 查找名称为name的类,返回的结果是java.lang.Class类的实例。

        - findLoadedClass(String name) 查找名称为name的已经被加载过的类,返回的结果是java.lang.Class类的实例。

        - defineClass(byte[] b, int off, int len) 把字节数组b中的内容转换成Java类,返回的结果是java.lang.Class类的实例。

        - resolveClass(Class<?> c) 连接指定的Java类

6、类加载器的代理模式

    代理模式

        - 交给其余加载器来加载指定的类

    双亲委托机制

        - 就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依此追溯,直到最高的爷爷辈的,若是父类加载器能够完成类加载任务,就成功返回;只有父类加载器没法完成此加载任务时,才本身去加载。

        - 双亲委托机制是为了保证Java核心库的类型安全。

            . 这种机制就保证不会出现用户本身能定义java.lang.Object类的状况。

        - 类加载器除了用于加载类,也是安全的最基本的屏障。

        - 双亲委托机制只是代理模式的一种

            . 并非全部的类加载器都采用双亲委托机制。

            . tomcat服务器类加载器也使用代理模式,所不一样的是它是首先尝试去加载某个类,若是找不到在代理给父类加载器。这与通常类加载器的顺序是相反的。

相关文章
相关标签/搜索