美文网首页
Java--ClassLoader解析

Java--ClassLoader解析

作者: 二妹是只猫 | 来源:发表于2019-04-03 09:35 被阅读0次

ClassLoader

类从编译到执行的过程

  • 编译器将Robot.java源文件编译为Robot.class字节码文件
  • ClassLoader将字节码转换成JVM中的Class<Robot>对象
  • JVM利用Class<Robot>对象实例化为Robot对象

定义: ClassLoader在java中有着非常重要的作用,它主要工作在Class装载的加载阶段,主要作用是从系统外部获得Class二进制数据流。它是java的核心组件,所有的class都是由ClassLoader进行加载的,ClassLoader负责通过将Class文件里的二进制数据流装载进系统,然后交给java虚拟机(JVM)进行连接、初始化等操作

种类

  • BootStrapClassLoader:C++编写,加载核心库java.*
  • ExtClassLoader:java编写,加载扩展库javax.*
  • AppClassLoader: java编写,加载程序所在目录(我们自己编写的代码)
  • 自定义ClassLoader: java编写,定制化加载

实现自定义ClassLoader

注意:使用defineClass生成Class文件,重写findClass制定加载class的路径

  • 首先使用javac生成一个Wali.class
public Class Wali{
  static{
    System.out.println("hello Wali");
  }
}
  • 编写自定义ClassLoader:
public class MyClassLoader extends ClassLoader {
    private String path;
    private String classLoaderName;

    public MyClassLoader(String path, String classLoaderName) {
        this.path = path;
        this.classLoaderName = classLoaderName;
    }

    //用于寻找类文件
    @Override
    public Class findClass(String name) {
        byte[] b = loadClassData(name);
        return defineClass(name, b, 0, b.length);
    }

    //用于加载类文件
    private byte[] loadClassData(String name) {
        name = path + name + ".class";
        InputStream in = null;
        ByteArrayOutputStream out = null;
        try {
            in = new FileInputStream(new File(name));
            out = new ByteArrayOutputStream();
            int i = 0;
            while ((i = in.read()) != -1) {
                out.write(i);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
                in.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return out.toByteArray();
    }
}
  • 运行:
public class ClassLoaderChecker {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        MyClassLoader m = new MyClassLoader("/Users/Desktop/", "myClassLoader");
        Class c = m.loadClass("Wali");
        System.out.println(c.getClassLoader());
        System.out.println(c.getClassLoader().getParent());
        System.out.println(c.getClassLoader().getParent().getParent());
        System.out.println(c.getClassLoader().getParent().getParent().getParent());
        c.newInstance();
    }
}
自定义ClassLoader

双亲委派机制

双亲委派机制
定义
首先自底向上查找class是否已经被装载了,若已加载直接返回,否则向上继续查找,若找到最顶层BoostrapClassLoader依然没有发现,说明该class没有被装载进内存成为Class对象,则需要自顶向下的尝试装载该class,若能装载成功则直接返回,否则依次向下寻找,直到最底层依然不能装载则抛出异常,
代码:ClassLoader
  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);
         1  if (c == null) {
                long t0 = System.nanoTime();
                try {
            2      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;
        }
    }

当加载字节码文件时,首先会加上同步锁防止别的类加载器去加载,在标记1处判断当前类加载器是否加载过,标记2处如果父类加载器不为空,调用父类加载器的loadClass方法迭代排查是否加载过。 这就符合了上面定义对双亲委派机制的介绍,从上方图片自定义ClassLoader中也能看到它们是这样的关系
为什么要使用双亲委派机制去加载类
避免同个字节码重复加载,浪费内存

类的加载方式

  • 隐式加载:new
  • 显示加载:loadClass、forName等

类的装载(加载)过程如下图:

loadClass和forName的区别

  • Class.forName得到的class是已经初始化完成了的 (MySQL加载驱动时,需要调用静态代码块完成一些操作)
  • ClassLoader.loadClass得到的class是还没有链接的。(用于Spring IoC中的延迟加载机制)

代码验证上面结论

  • 用来加载的类:
public class Robot {
    static {
        System.out.println("Hello Robot");
    }
}
  • 使用loadClass加载:
public class LoadDifference {
    public static void main(String[] args) throws ClassNotFoundException {
        ClassLoader cl = Robot.class.getClassLoader();
    }
}
loadClass

结论:没有执行静态代码块的代码,符合结论

  • 使用forName:
public class LoadDifference {
    public static void main(String[] args) throws ClassNotFoundException {
       Class r = Class.forName("com.interview.javabasic.reflect.Robot");
    }
}
forName

相关文章

网友评论

      本文标题:Java--ClassLoader解析

      本文链接:https://www.haomeiwen.com/subject/cvxabqtx.html