码迷,mamicode.com
首页 > 其他好文 > 详细

JVM-类加载器

时间:2017-11-15 21:45:19      阅读:251      评论:0      收藏:0      [点我收藏+]

标签:两种   执行   obj   rom   play   其他   dos   cli   进入   

类加载器

  对于虚拟机的角度来看,只存在两种类加载器: 启动类加载器(Brootstrap ClassLoader)和“其他类加载器”。启动类加载器是由C++写的,属于虚拟机的一部分,其他类加载器都是由java语言实现,独立于虚拟机外部,全部继承自抽象类java.lang.ClassLoader。

  从开发的角度来看,有三种类加载器:

  1)启动类加载器(Bootstrap ClassLoader):这个类加载器主要是负责加载${JAVA_HOME}/lib目录的jar(比如rt.jar、resources.jar)或者被-Xbootclasspath参数所指定的路径中的jar。

  (调用ClassLoader类的loadClass方法加载一个类,并不是对类的主动使用,不会导致类的初始化。)

  2)扩展类加载器(Extension ClassLoader):它负责加载${JAVA_HOME}/lib/ext目录或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器。

  3)应用类加载器(Application ClassLoader):这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,所以又叫系统类加载器。它负责加载用户类路径(ClassPath)上所指定的类库。

  类加载器的层次结构:

  技术分享                                技术分享                          

 ClassLoader加载类的原理:

  ClassLoader使用的是双亲委托模型来搜索类的,每个ClassLoader实例都有一个父类加载器的引用(不是继承的关系,是一个包含的关系),虚拟机内置的类加载器(Bootstrap ClassLoader)本身没有父类加载器,但可以用作其它ClassLoader实例的的父类加载器。当一个ClassLoader实例需要加载某个类时,它会试图亲自搜索某个类之前,先把这个任务委托给它的父类加载器,这个过程是由上至下依次检查的,首先由最顶层的类加载器Bootstrap ClassLoader试图加载,如果没加载到,则把任务转交给Extension ClassLoader试图加载,如果也没加载到,则转交给App ClassLoader 进行加载,如果它也没有加载得到的话,则返回给委托的发起者,由它到指定的文件系统或网络等URL中加载该类。如果它们都没有加载到这个类时,则抛出ClassNotFoundException异常。否则将这个找到的类生成一个类的定义,并将它加载到内存当中,最后返回这个类在内存中的Class实例对象。

 为什么要用双亲委托模型:

  因为这样可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。考虑到安全因素,我们试想一下,如果不使用这种委托模式,那我们就可以随时使用自定义的String来动态替代java核心api中定义的类型,这样会存在非常大的安全隐患,而双亲委托的方式,就可以避免这种情况,因为String已经在启动时就被引导类加载器(Bootstrcp ClassLoader)加载,所以用户自定义的ClassLoader永远也无法加载一个自己写的String,除非你改变JDK中ClassLoader搜索类的默认算法。

JVM在搜索类的时候,是如何叛党两个class是相同的呢?

  比较两个类是否“相等”,只有这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来自同一个.class文件,被同一个虚拟机加载,只要加载他们的类加载器不同,这两个类就必定不相等。这里的相等包括Class对象的equals()方法、isAssignableFrom()方法、isInstance()方法的返回结果,也包括instanceof关键字做对象所属关系判定等情况。例如,我们可以编写一个类加载器,它可以拒绝加载没有标记为"paid for"的类。

  如果要编写自己的类加载器,只需要继承抽象类 ClassLoader 然后重写findClass()方法。ClassLoader 中的 loadClass() 方法用于将类的加载操作委托给其父类加载器去进行,只有当该类尚未加载并且父类加载器也无法加载该类时,才调用findClass()方法。

  

自定义类加载器

  我们可以编写自己的用于特殊目的的类加载器,这使得我们可以在向虚拟机传递字节码之前执行定制的检查。

 

import java.io.*;

/**
 *  @ProjectName: base-project 
 *  @Description: 实现自己的类加载器
 */
public class MyClassLoader extends ClassLoader {
    private String name; //类加载器的名字
    private String path = "D:\\"; //加载类的路径
    private final String fileType = ".class"; //class文件的扩展名

    public MyClassLoader(String name) {
        super();
        this.name = name;
    }

    public MyClassLoader(ClassLoader parent, String name) {
        super(parent);
        this.name = name;
    }

    @Override
    public String toString() {
        return "MyClassLoader{" +
                "name=‘" + name + ‘\‘‘ +
                ‘}‘;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

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

    private byte[] loadClassData(String name) {
        InputStream is = null;
        byte[] data = null;
        ByteArrayOutputStream baos = null;

        try {
            this.name = this.name.replace(".", "\\");
            //流读取文件
            is = new FileInputStream(new File(path + name + fileType));
            baos = new ByteArrayOutputStream();
            int ch = 0;
            while(-1 != (ch = is.read())) {
                baos.write(ch);
            }

            data = baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                baos.close();
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return data;
    }

    public static void test(ClassLoader loader) throws Exception{
        Class clazz = loader.loadClass("Sample");
        Object object = clazz.newInstance();
    }

    public static void main(String[] args) throws Exception {
        MyClassLoader loader1 = new MyClassLoader("loader1");
        loader1.setPath("D:\\myapp\\serverlib\\");
        MyClassLoader loader2 = new MyClassLoader(loader1, "loader2");
        loader2.setPath("D:\\myapp\\clientlib\\");
        MyClassLoader loader3 = new MyClassLoader(null, "loader3");
        loader3.setPath("D:\\myapp\\otherlib\\");

        test(loader2);
//        System.out.println("==================");
        test(loader3);

//        Class clazz = loader1.loadClass("Sample");
//        Object obj = clazz.newInstance(); //创建一个Sample类的对象
//        Sample sample = (Sample)obj;
//        System.out.println(sample.v1);
    }

}

 

怎么运行这个程序来观察类加载过程呢?

 1、在D: 盘下分别建立文件夹

  D:\myapp\serverlib\
  D:\myapp\clientlib\
  D:\myapp\otherlib\
  D:\myapp\syslib\

2、新建Dog.java、Sample.java

public class Dog {
    public Dog() {
        System.out.println("Dog is loaded by : " + this.getClass().getClassLoader());
    }
}

 

public class Sample {
    public int v1 = 1;
    
    public Sample() {
        System.out.println("Sample is loaded by: " + this.getClass().getClassLoader());
        
        new Dog();
    }
}

 

3、把MyClassLoader.class、Dog.class、Sample.class三个.class文件分别放到(注意:要把代码中的package去掉)

技术分享

  这个时候 clientlib目录和other目录没有任何的.class文件

public static void main(String[] args) throws Exception {
        MyClassLoader loader1 = new MyClassLoader("loader1");
        loader1.setPath("D:\\myapp\\serverlib\\");
        MyClassLoader loader2 = new MyClassLoader(loader1, "loader2");
        loader2.setPath("D:\\myapp\\clientlib\\");
        MyClassLoader loader3 = new MyClassLoader(null, "loader3");
        loader3.setPath("D:\\myapp\\otherlib\\");

        test(loader2);
//        System.out.println("==================");
        test(loader3);

 

4、在windows环境下进入DOS命令窗口进入到 D:\myapp\syslib 目录下

5、运行 java MyClassLoader.class 看输入

技术分享

为什么会有这种输出呢?
先是bootstrap类加载器加载,发现没有找到,然后是Extentian类加载器加载发现也没有找到,App类加载器加载也没有找到,然后loader1类加载器加载发现找到了,就停止加载了。

 https://gitee.com/play-happy/base-project

参考:

[1] 《深入理解Java虚拟机:JVM高级特性与最佳实践》,周志明 ,机械工业出版社

[2] 《Java 核心技术卷》,机械工业出版社

[3] 博客,http://blog.csdn.net/xyang81/article/details/7292380#reply

JVM-类加载器

标签:两种   执行   obj   rom   play   其他   dos   cli   进入   

原文地址:http://www.cnblogs.com/happyflyingpig/p/7834039.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!