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启动时 被标明为 启动类的类 * 动态语言支持: * * 被动使用: * 除主动使用外,都是被动使用; * * 主动使用与被动使用的区别: * 被动使用 不会导致 类的初始化; * */