JVM之类加载器

1. 定义

类加载器(Class Loader),顾名思义就是把类文件加载到虚拟机中,正常点的描述:“通过一个类的全限定名来获取描述该类的二进制字节流”。

类加载器是Java语言的一项创新,最开始是为了满足Java Applet的需求而设计的。类加载器目前在层次划分、程序热部署和代码加密等领域经常使用。

2.类加载器种类

JVM为我们默认提供了系统类加载器(这里针对JDK1.8),包括:

Bootstrap ClassLoader(系统类加载器)

Extension ClassLoader(扩展类加载器)

Application ClassLoader(应用程序类加载器)

Customer ClassLoader(自定义加载器)

3、类加载器之间的关系

先看一张图

从这个图可能会认为类加载器的父子是继承关系,其实不然,类加载器的这种分层(优先级定义的父子)关系是为了安全的加载类库设计的,并没有继承关系,只是为了安全的加载类而设定的逻辑关系。

4. 每种类加载器的加载范围

类加载器的分层既然是为了安全,那就应该有指定的范围,自定义加载器在加载类时会委托AppClassLoader去加载,而AppClassLoader又会委托ExtClassLoader去加载,ExtClassLoader又会委托BootstrapClassLoader去加载,这就是上图中的红色箭头过程。

加载的时候一定是先从Bootstrap加载器开始,如果检查要加载的类没有找到,则又会让子类ExtClassLoader去加载,依次类推,直到最后由自定义类加载器加载或报ClassNotFoundException错误。

那么每种类加载器负责的范围是什么呢?

sun.misc.Launcher类中源码对这三类加载器负责的范围做了说明
package sun.misc;

public class Launcher {
    private static URLStreamHandlerFactory factory = new Launcher.Factory();
    private static Launcher launcher = new Launcher();
①   private static String bootClassPath = System.getProperty("sun.boot.class.path");
    private ClassLoader loader;
    private static URLStreamHandler fileHandler;
...
...
...
    static class AppClassLoader extends URLClassLoader {
        final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);

        public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
②           final String var1 = System.getProperty("java.class.path");
            final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
            return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() {
                public Launcher.AppClassLoader run() {
                    URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);
                    return new Launcher.AppClassLoader(var1x, var0);
                }
            });
        }
...
...
...
        public ExtClassLoader(File[] var1) throws IOException {
            super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);
            SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
        }

        private static File[] getExtDirs() {
③           String var0 = System.getProperty("java.ext.dirs");
            File[] var1;
            if (var0 != null) {
                StringTokenizer var2 = new StringTokenizer(var0, File.pathSeparator);
                int var3 = var2.countTokens();
                var1 = new File[var3];

                for(int var4 = 0; var4 < var3; ++var4) {
                    var1[var4] = new File(var2.nextToken());
                }
            } else {
                var1 = new File[0];
            }

            return var1;
        }

注意上面①②③位置的标注,用本地的JDK验证下,结果刚好符合预期。

public class test{
    public static void main(String[] args){

        String classPath = System.getProperty("sun.boot.class.path");
        System.out.println(classPath.replaceAll(";",System.lineSeparator()));

        System.out.println("------------------------------------------");
        String extPath = System.getProperty("java.ext.dirs");
        System.out.println(extPath.replaceAll(";",System.lineSeparator()));

        System.out.println("------------------------------------------");
        String appPath = System.getProperty("java.class.path");
        System.out.println(appPath.replaceAll(";",System.lineSeparator()));
    }
}

5. 类加载器关系验证

下面举一个小例子进行加载器之间的关系说明

在非classpath目录下我编译了一个类myname.class,对应的JAVA源码如下

public class myname{
	public myname(){

	}
	public void printClassName(){
		System.out.println("myname class's ClassLoader is:"+this.getClass().getClassLoader());
		System.out.println("=============");
	}
}

下面自定义了一个类加载器

package com.example.demo;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class MyClassLodder extends ClassLoader{
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        File f = new File("E:\\projs\\demo\\target\\classes\\com\\example\\demo\\",name.concat(".class"));
        try {
            FileInputStream fileInputStream = new FileInputStream(f);
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            int index = 0;
            while((index=fileInputStream.read()) != -1){
                byteArrayOutputStream.write(index);
            }
            byte[] bytes = byteArrayOutputStream.toByteArray();
            byteArrayOutputStream.close();
            fileInputStream.close();
            return defineClass(name,bytes,0,bytes.length);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return super.findClass(name);
    }

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        MyClassLodder myClassLodder = new MyClassLodder();
        Class clazz = myClassLodder.loadClass("myname");
        String s = "printClassName";
        Method method = clazz.getMethod(s);
        Object o = clazz.newInstance() ;
        method.invoke(o);
        System.out.println("myClassLodder类的加载器是:"+myClassLodder.getClass().getClassLoader());
        System.out.println("myClassLodder类的父加载器是:"+myClassLodder.getClass().getClassLoader().getParent());
        System.out.println("myClassLodder类的父加载器的加载器是:"+myClassLodder.getParent().getClass().getClassLoader());
        System.out.println("myClassLodder类的父加载器的父加载器的父加载器是:"+myClassLodder.getParent().getClass().getClassLoader().getParent());
    }
}

打印结果如下:

myname class's ClassLoader is:com.example.demo.MyClassLodder@3cd1a2f1
=============
myClassLodder类的加载器是:sun.misc.Launcher$AppClassLoader@18b4aac2
myClassLodder类的父加载器是:sun.misc.Launcher$ExtClassLoader@49476842
myClassLodder类的父加载器的加载器是:null
Exception in thread "main" java.lang.NullPointerException
	at com.example.demo.MyClassLodder.main(MyClassLodder.java:40)

Process finished with exit code 1

最后一行代码为什么报错呢,是因为最高级的BootstrapClassLoader是本地方法,是用C++写的,没有对应的父加载器。

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值