• java中类加载器入门


    前言

    顾名思义,类加载器就是负责类的加载的,从内存中,class文件中,jar包中等渠道加载,对于任意一个class,都需要由加载它的类加载器和这个类本身确定其在JVM中的唯一性。

    java内置类加载器

    java中内置了3种类加载器

    根类加载器

    又叫做Bootstrap类加载器,是最顶层的类加载器,没有父类加载器,是C++语言编写的,主要负责java中核心类库的加载,如java.lang包下所有类。

    public class Test {
    
      public static void main(String[] args) {
        System.out.println(String.class.getClassLoader());
        String property = System.getProperty("sun.boot.class.path");
        for (String path : property.split(";")) {
          System.out.println(path);
        }
      }
    }
    

    输出为

    null
    D:javajdkjdk1.8.0_181jrelib
    esources.jar
    D:javajdkjdk1.8.0_181jrelib
    t.jar
    D:javajdkjdk1.8.0_181jrelibsunrsasign.jar
    D:javajdkjdk1.8.0_181jrelibjsse.jar
    D:javajdkjdk1.8.0_181jrelibjce.jar
    D:javajdkjdk1.8.0_181jrelibcharsets.jar
    D:javajdkjdk1.8.0_181jrelibjfr.jar
    D:javajdkjdk1.8.0_181jreclasses
    

    根类加载器获取不到引用,所以为null,常用的类如String都是在rt.jar这个jar中的。

    扩展类加载器

    由java语言实现,父类加载器为根类加载器,主要加载JAVA_HOME下jrelibext下的目录

    import jdk.nashorn.tools.Shell;
    
    public class Test {
    
      public static void main(String[] args) {
        System.out.println(Shell.class.getClassLoader());
        String property = System.getProperty("java.ext.dirs");
        for (String path : property.split(";")) {
          System.out.println(path);
        }
      }
    }
    

    输出为

    sun.misc.Launcher$ExtClassLoader@6d6f6e28
    D:javajdkjdk1.8.0_181jrelibext
    C:WINDOWSSunJavalibext
    

    Shell类就是nashorn.jar中的一个类,类加载器确实为扩展类加载器。

    static class ExtClassLoader extends URLClassLoader {
        private static volatile Launcher.ExtClassLoader instance;
    
        public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
          if (instance == null) {
            Class var0 = Launcher.ExtClassLoader.class;
            synchronized(Launcher.ExtClassLoader.class) {
              if (instance == null) {
                instance = createExtClassLoader();
              }
            }
          }
    
          return instance;
        }
    private static Launcher.ExtClassLoader createExtClassLoader() throws IOException {
          try {
            return (Launcher.ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction<Launcher.ExtClassLoader>() {
              public Launcher.ExtClassLoader run() throws IOException {
                File[] var1 = Launcher.ExtClassLoader.getExtDirs();
                int var2 = var1.length;
    
                for(int var3 = 0; var3 < var2; ++var3) {
                  MetaIndex.registerDirectory(var1[var3]);
                }
    
                return new Launcher.ExtClassLoader(var1);
              }
            });
          } catch (PrivilegedActionException var1) {
            throw (IOException)var1.getException();
          }
        }
    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;
        }
    }
    

    扩展类加载器的定义如上,使用了双重锁判断的单例写法。

    系统类加载器

    也是java语言实现的,父类加载器为扩展类加载器,加载classpath下的类,如我们引入的第三方jar包和我们自己定义的类。

    public class Test {
    
      public static void main(String[] args) {
        System.out.println(Test.class.getClassLoader());
        String property = System.getProperty("java.class.path");
        for (String path : property.split(";")) {
          System.out.println(path);
        }
      }
    }
    

    输出为

    sun.misc.Launcher$AppClassLoader@18b4aac2
    D:javajdkjdk1.8.0_181jrelibcharsets.jar
    D:javajdkjdk1.8.0_181jrelibdeploy.jar
    D:javajdkjdk1.8.0_181jrelibextaccess-bridge-64.jar
    D:javajdkjdk1.8.0_181jrelibextcldrdata.jar
    D:javajdkjdk1.8.0_181jrelibextdnsns.jar
    D:javajdkjdk1.8.0_181jrelibextjaccess.jar
    D:javajdkjdk1.8.0_181jrelibextjfxrt.jar
    D:javajdkjdk1.8.0_181jrelibextlocaledata.jar
    D:javajdkjdk1.8.0_181jrelibext
    ashorn.jar
    D:javajdkjdk1.8.0_181jrelibextsunec.jar
    D:javajdkjdk1.8.0_181jrelibextsunjce_provider.jar
    D:javajdkjdk1.8.0_181jrelibextsunmscapi.jar
    D:javajdkjdk1.8.0_181jrelibextsunpkcs11.jar
    D:javajdkjdk1.8.0_181jrelibextzipfs.jar
    D:javajdkjdk1.8.0_181jrelibjavaws.jar
    D:javajdkjdk1.8.0_181jrelibjce.jar
    D:javajdkjdk1.8.0_181jrelibjfr.jar
    D:javajdkjdk1.8.0_181jrelibjfxswt.jar
    D:javajdkjdk1.8.0_181jrelibjsse.jar
    D:javajdkjdk1.8.0_181jrelibmanagement-agent.jar
    D:javajdkjdk1.8.0_181jrelibplugin.jar
    D:javajdkjdk1.8.0_181jrelib
    esources.jar
    D:javajdkjdk1.8.0_181jrelib
    t.jar
    C:Program FilesJetBrainsIntelliJ IDEA 2019.1.3libidea_rt.jar
    

    系统类加载器的定义

    public Launcher() {
        Launcher.ExtClassLoader var1;
        try {
          var1 = Launcher.ExtClassLoader.getExtClassLoader();
        } catch (IOException var10) {
          throw new InternalError("Could not create extension class loader", var10);
        }
    
        try {
          this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
        } catch (IOException var9) {
          throw new InternalError("Could not create application class loader", var9);
        }
    
        Thread.currentThread().setContextClassLoader(this.loader);
        String var2 = System.getProperty("java.security.manager");
        if (var2 != null) {
          SecurityManager var3 = null;
          if (!"".equals(var2) && !"default".equals(var2)) {
            try {
              var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
            } catch (IllegalAccessException var5) {
            } catch (InstantiationException var6) {
            } catch (ClassNotFoundException var7) {
            } catch (ClassCastException var8) {
            }
          } else {
            var3 = new SecurityManager();
          }
    
          if (var3 == null) {
            throw new InternalError("Could not create SecurityManager: " + var2);
          }
    
          System.setSecurityManager(var3);
        }
    
      }
    

    先创建扩展类加载器,将扩展类加载器作为父类加载器创建系统类加载器,并设置到当前线程的上下文类加载器中。

    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);
            }
          });
        }
    
        AppClassLoader(URL[] var1, ClassLoader var2) {
          super(var1, var2, Launcher.factory);
          this.ucp.initLookupCache(this);
        }
    

    类加载流程

    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;
            }
        }
    
    1. 首先检查该类是否已经加载过,如果是,直接返回已经加载过的类
    2. 父类加载器存在,委托父类加载器加载,不存在,委托根类加载器
    3. 都不能加载的话,由当前类加载器加载,重写findClass()方法
    protected Class<?> findClass(final String name)
            throws ClassNotFoundException
        {
            final Class<?> result;
            try {
                result = AccessController.doPrivileged(
                    new PrivilegedExceptionAction<Class<?>>() {
                        public Class<?> run() throws ClassNotFoundException {
                            String path = name.replace('.', '/').concat(".class");
                            Resource res = ucp.getResource(path, false);
                            if (res != null) {
                                try {
                                    return defineClass(name, res);
                                } catch (IOException e) {
                                    throw new ClassNotFoundException(name, e);
                                }
                            } else {
                                return null;
                            }
                        }
                    }, acc);
            } catch (java.security.PrivilegedActionException pae) {
                throw (ClassNotFoundException) pae.getException();
            }
            if (result == null) {
                throw new ClassNotFoundException(name);
            }
            return result;
        }
    

    URLClassLoader就是通过重写findClass()实现的。

    打破双亲委派机制

    双亲委派机制保证了越基础的类由越上层的类加载器加载,基础类之所以被称为“基础”,是因为它们总是作为被调用的API。但是,如果基础类要调用用户的代码,那该怎么办呢。一个典型的例子就是JDBC

    jdk提供了所有相关的接口,这些接口都是由根类加载器加载的,而第三方驱动的实现是由系统类加载器加载的,按照双亲委派机制,根类加载器不可能加载得到第三方驱动的实现,为了解决这个困境,java引入了一种不太优雅的设计,线程上下文类加载器,这就变成了父类加载器委派子类加载器去加载,打破了双亲委派机制。java中所有涉及到SPI的动作基本上都是采用的这种方式。



    参考

    类加载器如何打破双亲委派加载机制(SPI原理)

  • 相关阅读:
    PTA L2-023 图着色问题-前向星建图 团体程序设计天梯赛-练习集
    PTA L2-004 这是二叉搜索树吗?-判断是否是对一棵二叉搜索树或其镜像进行前序遍历的结果 团体程序设计天梯赛-练习集
    PTA L2-006 树的遍历-二叉树的后序遍历+中序遍历,输出层序遍历 团体程序设计天梯赛-练习集
    HDU1166敌兵布阵(线段树单点更新)
    洛谷P1019——单词接龙(DFS暴力搜索)
    洛谷P1309——迷宫(傻瓜DFS)
    CodeForce-791B Bear and Friendship Condition(并查集)
    傻子都能懂的并查集题解——HDU1232畅通工程
    洛谷P1309——瑞士轮(归并排序)
    洛谷P1583——魔法照片(结构体排序)
  • 原文地址:https://www.cnblogs.com/strongmore/p/14952775.html
Copyright © 2020-2023  润新知