JVM---类加载子系统

 

作用

/**
     *  【JVM-类加载子系统-作用】
     *              负责 从文件系统 或 网络中加载class文件;
     *              ClassLoader 只负责 class文件的加载,至于 是否可以运行,由 Execution Engine决定;
     *              加载的 类元数据信息 存放于 方法区的内存空间;
     *                  (除了类元数据信息外,方法区还会存放运行时常量池信息:字符串字面量、数字常量[这部分常量信息 是class文件中常量池部分的内存映射])
     */

  

类加载

  加载

    将类的二进制数据(通常是以.class文件形式存在的字节码)从磁盘或其他存储设备上读取到内存中、并创建代表该类的Class对象

  链接

    主要目的是  确保被加载的类信息符合Java语言规范,并且为方法执行做必要的准备工作;

  初始化
    执行类的初始化代码,包括静态变量赋值和静态块的执行;

/**
     *  【JVM-类加载子系统-类加载】
     *              加载loading:
     *                  a,通过 类的全限定名 获取定义此类的 二进制字节流;
     *                      加载class文件方式:
     *                          本地文件系统;
     *                          网络;
     *                          jar,war,zip包...
     *                          运行时计算生成:动态代理...
     *                          其他文件生成:JSP
     *                          ...
     *
     *                  b,将 定义类的 静态存储结构 转化为 方法区的运行时数据结构;
     *                  c,在 内存中 生成一个代表 该类的java.lang.Class对象,作为 方法区 这个类的各种元数据信息 的访问入口;
     *
     *
     *              链接linking:
     *                  a,验证verify:
     *                      文件格式校验
     *                          开头是否以0xCAFEBABE
     *                          ...
     *                      元数据验证
     *                          进行语义分析:是否有父类?是否继承final类?...
     *                      字节码验证
     *                      符号引用验证
     *
     *                  b,准备prepare:
     *                      为 static变量 在方法区 分配内存,并 赋初始值;
     *                      对于 final static 变量,编译时就已经设置初始值,准备阶段 显式 初始化;
     *
     *                  c,解析resolve:
     *                      将 常量池 中的 符号引用 转化为 直接引用;
     *
     *
     *              初始化initialization:
     *                  执行 <clinit>() 的过程;
     *
     *                      <clinit>():
     *                          1、来源:
     *                              javac编译器 自动收集 类中 所有 static 变量的 赋值内容 (包括 static{} 的内容);
     *
     *                          2、指令执行顺序 :
     *                              声明顺序 ;
     *                              eg:
     *                                      static {
     *                                          a = 2;
     *                                          System.out.println(a); // 报错:非法前向引用
     *                                      }
     *
     *                                      private static int a = 1;
     *
     *                                      先使用-后声明:
     *                                          static变量a 在linking-prepare时 已经赋初始值0,所以 初始化阶段执行<clinit>() 是可以的;
     *                                          【仅允许赋值,不允许调用】;
     *
     *                                      <clinit>():
     *                                          iconst_2
     *                                          putstatic
     *                                          iconst_1
     *                                          putstatic
     *                                          return
     *
     *                          3、如果 类 中没有 static 变量 、没有 static 代码块, javac编译后不会生成  <clinit>();
     *                              <init> :
     *                                  类的构造器;
     *                                  任何一个类声明后,内部至少存在一个类的构造器;
     *
     *                          4、执行 子类的 <clinit>() 前 ,必须 保证 父类的<clinit>()执行完毕;
     *
     *                          5、JVM 必须 保证 一个类的<clinit>() 在多线程 下 被 同步加锁;
     *
     */

  

类加载器

/**
     *  【JVM-类加载子系统-类加载器】
     *              JVM支持2种类型的类加载器:
     *                  引导类加载器Bootstrap ClassLoader:
     *                  自定义类加载器:
     *                      JVM规范中,将所有派生于ClassLoader的类加载器 称为 自定义类加载器;
     *
     *              sun.misc.Launcher 是一个JVM的入口应用;
     *                  public class Launcher {
     *                      static class AppClassLoader extends URLClassLoader {}
     *
     *                      static class ExtClassLoader extends URLClassLoader {}
     *                  }
     *
     *                  java.net.URLClassLoader
     *                      public class URLClassLoader extends SecureClassLoader implements Closeable {}
     *
     *                          java.security.SecureClassLoader
     *                              public class SecureClassLoader extends ClassLoader {}
     *
     *                                  java.lang.ClassLoader
     *                                      public abstract class ClassLoader {}
     *
     *
     *              引导类加载器、拓展类加载器、系统类加载器、用户自定义类加载器 的关系:
     *                  包含关系 (不是上下层、不是继承关系)
     *
     *                  类加载器之间的这种层级关系,称为 类加载器的双亲委派模型;
     *                      双亲委派模型 要求 除了顶层的启动类加载器外,其余的类加载器应有自己的父类加载器;
     *                      父子关系不会以继承的关系实现,而是使用组合关系来复用父类加载器;
     *
     *              a,引导类加载器Bootstrap ClassLoader
     *                  使用 C/C++实现,嵌套在JVM内部;
     *                  不继承自java.lang.ClassLoader,无父类加载器;
     *                  加载 拓展类加载器 、系统类加载器;
     *                  出于安全考虑,Bootstrap ClassLoader只加载java、javax、sun等开头的类;
     *                  作用:
     *                      加载 Java的核心类库
     *                          (java_home/jre/lib/rt.jar、resources.jar或sun.boot.class.path路径下的内容)
     *                          用于JVM自身需要的类;
     *
     *                          URL[] urLs = sun.misc.Launcher.getBootstrapClassPath().getURLs();
     *                          for(URL url : urLs){
     *                              System.out.println(url.toExternalForm());
     *                          }
     *
     *                          //file:/Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/resources.jar
     *                          //file:/Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/rt.jar
     *                          //file:/Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/sunrsasign.jar
     *                          //file:/Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/jsse.jar
     *                          //file:/Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/jce.jar
     *                          //file:/Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/charsets.jar
     *                          //file:/Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/jfr.jar
     *                          //file:/Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/classes
     *
     *              b,拓展类加载器ExtClassLoader
     *                  使用Java实现,sun.misc.Launcher.ExtClassLoader;
     *                  继承自ClassLoader;
     *                  作用:
     *                      加载 jre/lib/ext 目录下的类库;
     *
     *                      String property = System.getProperty("java.ext.dirs");
     *                      for(String path : property.split(":")){
     *                          System.out.println(path);
     *                      }
     *
     *                      // /Users/an/Library/Java/Extensions
     *                      // /Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/ext
     *                      // /Library/Java/Extensions
     *                      // /Network/Library/Java/Extensions
     *                      // /System/Library/Java/Extensions
     *                      // /usr/lib/java
     *
     *
     *              c,系统类加载器
     *                  使用Java实现,sun.misc.Launcher.AppClassLoader;
     *                  继承自ClassLoader;
     *                  作用:
     *                      加载 classpath或java.class.path 下 用户 自定义类;
     *
     *              d,用户自定义类加载器
     *                  为什么要自定义?
     *                      a,隔离 加载类;
     *                      b,修改 类的加载方式;
     *                      c,拓展 加载源;
     *                      d,防止 源码泄漏;
     *
     *                  自定义步骤:
     *                      第一种:
     *                          a,继承 java.lang.ClassLoader
     *                          b,重写 findClass()
     *
     *                      第二种:
     *                          如果没有过多的逻辑,可以直接 继承 java.net.URLClassLoader;
     *
     *              java.lang.ClassLoader
     *                  public abstract class ClassLoader {
     *
     *                      // 根据 class全限定名称 加载Class
     *                      public Class<?> loadClass(String name) throws ClassNotFoundException {}
     *                  }
     *
     */

  

public class Launcher {

        private ClassLoader loader;
        private static String bootClassPath = System.getProperty("sun.boot.class.path");        // bootStrap类加载器 加载 sun.boot.class.path下的内容

        public Launcher() {
            ExtClassLoader var1;
            try {
                var1 = ExtClassLoader.getExtClassLoader();
            } catch (IOException var10) {
                throw new InternalError("Could not create extension class loader", var10);
            }

            try {
                this.loader = AppClassLoader.getAppClassLoader(var1);
            } catch (IOException var9) {
                throw new InternalError("Could not create application class loader", var9);
            }
        }

        static class ExtClassLoader extends URLClassLoader {

            public static ExtClassLoader getExtClassLoader() throws IOException {
                final File[] var0 = getExtDirs();

                try {
                    return (ExtClassLoader) AccessController.doPrivileged(new PrivilegedExceptionAction<ExtClassLoader>() {
                        public ExtClassLoader run() throws IOException {
                            int var1 = var0.length;

                            for(int var2 = 0; var2 < var1; ++var2) {
                                MetaIndex.registerDirectory(var0[var2]);
                            }

                            return new ExtClassLoader(var0);
                        }
                    });
                } catch (PrivilegedActionException var2) {
                    throw (IOException)var2.getException();
                }
            }

            private static File[] getExtDirs() {
                String var0 = System.getProperty("java.ext.dirs");              // 扩展类加载器 加载 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;
            }
        }

        static class AppClassLoader extends URLClassLoader {

            public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
                final String var1 = System.getProperty("java.class.path");                                  // 系统类加载器 加载 java.class.path下的内容
                final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
                return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<AppClassLoader>() {
                    public AppClassLoader run() {
                        URL[] var1x = var1 == null ? new URL[0] : sun.misc.Launcher.pathToURLs(var2);
                        return new Launcher.AppClassLoader(var1x, var0);
                    }
                });
            }
        }
    }

 

AppClassLoader

sun.misc.Launcher.AppClassLoader

 

ExtClassLoader

sun.misc.Launcher.ExtClassLoader

 

Class加载流程

sun.misc.Launcher.AppClassLoader#loadClass

public Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException {
            int var3 = var1.lastIndexOf(46);
            if (var3 != -1) {
                SecurityManager var4 = System.getSecurityManager();
                if (var4 != null) {
                    var4.checkPackageAccess(var1.substring(0, var3));
                }
            }

            if (this.ucp.knownToNotExist(var1)) {
                Class var5 = this.findLoadedClass(var1);
                if (var5 != null) {
                    if (var2) {
                        this.resolveClass(var5);
                    }

                    return var5;
                } else {
                    throw new ClassNotFoundException(var1);
                }
            } else {
                return super.loadClass(var1, var2);   
            }
        }

  

super.loadClass(var1, var2)

 

java.lang.ClassLoader#loadClass(java.lang.String, boolean)

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;
        }
    }

  

 

java.lang.ClassLoader#loadClass(java.lang.String, boolean)

java.lang.ClassLoader#findBootstrapClassOrNull    使用bootstrap累加载器加载

java.lang.ClassLoader#findClass             使用自定义类加载器加载

 

双亲委派机制

/**
     *  【JVM-类加载子系统-双亲委派机制】
     *              JVM 对 class文件 采用的是 按需加载(使用该类时,再进行加载);
     *              加载 某个class文件时,采用 双亲委派机制 (把 加载请求 交由 上级加载器 加载);
     *                  如果 上级 类加载器 不能完成 类的加载,才由 本级类加载器 去加载;
     *
     *              优势:
     *                  避免类的重复加载;
     *                  保护程序安全,防止 核心 API 被篡改;
     *                      eg:
     *                          自定义 java.lang.String类...
     */

  

其他

/**
     *  【JVM-类加载子系统-其他】
     *      在同一个JVM中,表示2个class对象是否一致的必要条件: 类全限定名 + ClassLoader
     *          a, 类的全限定名称相同
     *          b, 加载该类的ClassLoader相同
     *
     *      类的主动使用和被动使用
     *          主动使用:
     *              创建类的实例;
     *              访问某个类或接口的static变量 或 对static变量赋值;
     *              调用类的static方法
     *              反射(Class.forName(""))
     *              初始化一个类的子类
     *              JVM启动时 被标明为 启动类的类
     *              动态语言支持:
     *
     *          被动使用:
     *              除主动使用外,都是被动使用;
     *
     *      主动使用与被动使用的区别:
     *          被动使用 不会导致 类的初始化;
     *
     */

  

posted on 2022-03-25 15:37  anpeiyong  阅读(25)  评论(0编辑  收藏  举报

导航