Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.
类或接口的解析 假设当前代码所处的类为 D,如果要把一个从未解析过的符号引用 N 解析为一个类或接口 C 的直接引用,那虚拟机完成整个解析的过程需要包括以下 3 个步骤:
如果 C 不是一个数组类型,那虚拟机将会把代表 N 的全限定名传递给 D 的类加载器去加载这个 类 C。在加载过程中,由于元数据验证、字节码验证的需要,又可能触发其他相关类的加载动作,例如加载这个类的父类或实现的接口。 如果 C 是一个数组类型,并且数组的元素类型为对象,也就是 N 的描述符会是类 似“ [Ljava/lang/Integer”的形式,那将会按照第一点的规则加载数组元素类型。如果 N 的描述符如前面所假设的形式,需要加载的元素类型就是“java.lang.Integer”,接着由虚拟机生成一个代表该数组维度和元素的数组对象。 如果上面两步没有出现任何异常,那么 C 在虚拟机中实际上已经成为一个有效的类或接口了, 但在解析完成前还要进行符号引用验证,确认 D 是否具备对 C 的访问权限。如果发现不具备访问权限,将抛出 java.lang.IllegalAccessError 异常。 2. 字段解析 要解析一个未被解析过的字段符号引用,首先将会对字段表内 class_index 项中索引的 CONSTANT_Class_info 符号引用进行解析,也就是字段所属的类或接口的符号引用。如果解析成功完成,那把这个字段所属的类或接口用 C 表示,《Java 虚拟机规范》要求按照如下步骤对 C 进行后续字段的搜索:
如果 C 本身就包含了简单名称和字段描述符都与目标相匹配的字段,则返回这个字段的直接引用,查找结束。 否则,如果在 C 中实现了接口,将会按照继承关系从下往上递归搜索各个接口和它的父接口, 如果接口中包含了简单名称和字段描述符都与目标相匹配的字段,则返回这个字段的直接引用,查找结束。 否则,如果 C 不是 java.lang.Object 的话,将会按照继承关系从下往上递归搜索其父类,如果在父类中包含了简单名称和字段描述符都与目标相匹配的字段,则返回这个字段的直接引用,查找结束。 否则,查找失败,抛出 java.lang.NoSuchFieldError 异常。 如果查找过程成功返回了引用,将会对这个字段进行权限验证,如果发现不具备对字段的访问权限,将抛出 java.lang.IllegalAccessError 异常。 3. 方法解析 方法解析的第一个步骤与字段解析一样,也是需要先解析出方法表的 class_index 项中索引的方法所属的类或接口的符号引用,如果解析成功,那么我们依然用 C 表示这个类,接下来虚拟机将会按照如下步骤进行后续的方法搜索:
由于 Class 文件格式中类的方法和接口的方法符号引用的常量类型定义是分开的,如果在类的 方法表中发现 class_index 中索引的 C 是个接口的话,那就直接抛出 java.lang.Incomp atibleClassChangeError 异常。 如果通过了第一步,在类 C 中查找是否有简单名称和描述符都与目标相匹配的方法,如果有则 返回这个方法的直接引用,查找结束。 否则,在类 C 的父类中递归查找是否有简单名称和描述符都与目标相匹配的方法,如果有则返回这个方法的直接引用,查找结束。 否则,在类 C 实现的接口列表及它们的父接口之中递归查找是否有简单名称和描述符都与目标相匹配的方法,如果存在匹配的方法,说明类 C 是一个抽象类,这时候查找结束,抛出 java.lang.AbstractMethodError 异常。 否则,宣告方法查找失败,抛出 java.lang.NoSuchMethodError。 最后,如果查找过程成功返回了直接引用,将会对这个方法进行权限验证,如果发现不具备对此方法的访问权限,将抛出 java.lang.IllegalAccessError 异常。 4. 接口方法解析 接口方法也是需要先解析出接口方法表的 class_index 项中索引的方法所属的类或接口的符号引用,如果解析成功,依然用 C 表示这个接口,接下来虚拟机将会按照如下步骤进行后续的接口方法搜索:
与类的方法解析相反,如果在接口方法表中发现 class_index 中的索引 C 是个类而不是接口,那 么就直接抛出 java.lang.IncompatibleClassChangeError 异常。 否则,在接口 C 中查找是否有简单名称和描述符都与目标相匹配的方法,如果有则返回这个方法的直接引用,查找结束。 否则,在接口 C 的父接口中递归查找,直到 java.lang.Object 类(接口方法的查找范围也会包括 Object 类中的方法)为止,看是否有简单名称和描述符都与目标相匹配的方法,如果有则返回这个方法的直接引用,查找结束。 否则,宣告方法查找失败,抛出 java.lang.NoSuchMethodError 异常。
if (c == null) { // 父类加载器无法加载,则自身加载 longt1= System.nanoTime(); // 实现自定义类加载器时,一般需要实现findClass方法 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; } }
// 其他一些方法 //用于将 byte字节流解析成 JVM 能够识别的 Class 对象。有了这个方法意味着我们不仅可以通过 .class文件实例化对象,还可以通过其他方式实例化对象,例如通过网络接收到一个类的字节码。,一般在重写findClass方法时加载完字节码后再调用defineClass defineClass()
//用于对 Class 进行 链接,也就是把单一的 Class 加入到有继承关系的类树中。如果你想在类被加载到 JVM 中时就被链接(Link),那么可以在调用 defineClass()之后紧接着调用一个 resolveClass()方法,当然你也可以选择让 JVM 来解决什么时候才链接这个类(通常是真正被实实例化的时候),默认不调用,代码验证,观察TODO resolveClass()
//类命名空间 // The classes loaded by this class loader. The only purpose of this table // is to keep the classes from being GC'ed until the loader is GC'ed. privatefinal Vector<Class<?>> classes = newVector<>(); // Invoked by the VM to record every loaded class with this loader. voidaddClass(Class<?> c) { classes.addElement(c); }
/** * The class loader that is used to find resources in modules defined to * the boot class loader. It is not used for class loading. */ privatestaticclassBootClassLoaderextendsBuiltinClassLoader { BootClassLoader(URLClassPath bcp) { super(null, null, bcp); }
/** * The platform class loader, a unique type to make it easier to distinguish * from the application class loader. */ privatestaticclassPlatformClassLoaderextendsBuiltinClassLoader { static { if (!ClassLoader.registerAsParallelCapable()) thrownewInternalError(); }
/** * The application class loader that is a {@code BuiltinClassLoader} with * customizations to be compatible with long standing behavior. */ privatestaticclassAppClassLoaderextendsBuiltinClassLoader { static { if (!ClassLoader.registerAsParallelCapable()) thrownewInternalError(); }
@Override protected Class<?> loadClass(String cn, boolean resolve) throws ClassNotFoundException { // for compatibility reasons, say where restricted package list has // been updated to list API packages in the unnamed module. @SuppressWarnings("removal") SecurityManagersm= System.getSecurityManager(); if (sm != null) { inti= cn.lastIndexOf('.'); if (i != -1) { sm.checkPackageAccess(cn.substring(0, i)); } }
/** * Called by the VM to support dynamic additions to the class path * * @see java.lang.instrument.Instrumentation#appendToSystemClassLoaderSearch */ voidappendToClassPathForInstrumentation(String path) { appendClassPath(path); }
/** * Called by the VM to support define package for AppCDS */ protected Package defineOrCheckPackage(String pn, Manifest man, URL url) { returnsuper.defineOrCheckPackage(pn, man, url); }
/** * Called by the VM, during -Xshare:dump */ privatevoidresetArchivedStates() { setClassPath(null); } }
protected Class<?> loadClassOrNull(String cn, boolean resolve) { synchronized (getClassLoadingLock(cn)) { // check if already loaded Class<?> c = findLoadedClass(cn); if (c == null) { // find the candidate module for this class //模块是否加载过 LoadedModuleloadedModule= findLoadedModule(cn); if (loadedModule != null) { // package is in a module //使用模块加载器加载 BuiltinClassLoaderloader= loadedModule.loader(); if (loader == this) { //如果模块加载器是自己,就直接加载 if (VM.isModuleSystemInited()) { c = findClassInModuleOrNull(loadedModule, cn); } } else { // delegate to the other loader c = loader.loadClassOrNull(cn); } } else { // check parent if (parent != null) { c = parent.loadClassOrNull(cn); } // check class path if (c == null && hasClassPath() && VM.isModuleSystemInited()) { c = findClassOnClassPathOrNull(cn); } }
} if (resolve && c != null) resolveClass(c); return c; } }