我刚刚开始学习Java的内部架构.我已经粗略地理解了类加载的概念,它在jvm
运行时加载所需的类,ClassNotFoundException
在没有找到类时抛出,特定的类加载器加载类引用的类.
有人可以清楚地解释类加载的流程,即下面的示例Java代码中的引导类加载和用户定义的类加载的顺序.
import java.io.File; public class Sample { public static void main(String[] args) { String fileName = "sample"; File file = new File(fileName); file.isFile(); } }
我还从一个参考资料中学到了" classloader
维护它加载的类的名称空间".通过名称空间,这是否意味着类的文字名称?也有人可以解释一下这个含义/优点吗?
您将按Sample
如下方式运行您的课程
> java Sample
对于小魔术,检查-verbose:class
选项的输出,你会看到大量的以下行..
[Opened C:\jdk1.6.0_14\jre\lib\rt.jar] [Loaded java.lang.Object from C:\jdk1.6.0_14\jre\lib\rt.jar] [Loaded java.io.Serializable from C:\jdk1.6.0_14\jre\lib\rt.jar] [Loaded java.lang.Comparable from C:\jdk1.6.0_14\jre\lib\rt.jar] . . . . . . [Loaded java.security.cert.Certificate from C:\jdk1.6.0_14\jre\lib\rt.jar] [Loaded Sample from file:/D:/tmp/] [Loaded java.lang.Shutdown from C:\jdk1.6.0_14\jre\lib\rt.jar] [Loaded java.lang.Shutdown$Lock from C:\jdk1.6.0_14\jre\lib\rt.jar]
\jre\lib\rt.jar
在类加载Bootstrap
器(或Primordial)加载类之前,您会看到一堆类被加载.这些是运行任何由Bootstrap加载的Java程序的先决条件.
另一组jar由Extension
类加载器加载.在这个特定的例子中,不需要lib中的任何类,\jre\lib\ext
因此它没有被加载.但Extension类加载器专门分配了从扩展lib加载类的任务.
编辑:除了标准平台java类之外,Sun/Oracle还提供了一组用于扩展平台核心API的jar .放置在扩展lib文件夹中的jar自动放在类路径中,因此不需要显式地包含在类路径中.这是关于同一主题的好官方文章.
最后,在Bootstrap和Extension完成加载后,您的类Sample
由Application
类加载器加载.
每当启动新的JVM时,引导类加载器都负责将关键Java类(从java.lang
包)和其他运行时类首先加载到内存中.引导类加载器是所有其他类加载器的父级.因此,它是唯一没有父母的人.
接下来是扩展类加载器.它有bootstrap类加载器作为父类,负责从.jar
保存在java.ext.dirs
路径中的所有文件加载类- 无论JVM的类路径如何,它们都可用.
从开发人员的角度来看,第三个也是最重要的类加载器是系统类路径类加载器,它是扩展类加载器的直接子代.它从CLASSPATH
环境变量,java.class.path
系统属性或-classpath
命令行选项指定的目录和jar文件中加载类.
ClassLoader命名空间
在Java中,使用唯一标识的类,
ClassLoader + Class
因为同一个类可以由两个不同的类加载器加载.
Class A loaded by ClassLoader A != Class A loaded by ClassLoader B
它有什么用?
为不同的类加载器定义不同的保护和访问策略很有帮助.举一个使用不同类加载器加载的applet的例子,你不希望第三方应用程序访问你的资源.因此,对于安全性,维护不同名称空间非常重要.
JVM在加载类的permgen区域中维护一个运行时池.每当引用类时,默认类加载器都会在类路径中找到该类并将其加载到此池中.这不是特定于JDK中提供的用户定义的类或类.引用类时,它将加载到内存中.
由ClassLoader加载的类内部存储在ClassLoader实例中
// 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. private final Vector<Class<?>> classes = new Vector<>();
当需要将类添加到内存后,调用以下函数:
// Invoked by the VM to record every loaded class with this loader. void addClass(Class c) { classes.addElement(c); }
找到了有关类加载器如何工作的有用图表.
该Java虚拟机通过创建一个初始类,这是在一种实现依赖性方式的规定,使用引导类加载器(启动§5.3.1).然后,Java虚拟机链接初始类,初始化它和在其中声明的静态实例变量,最后调用公共类方法void main(String []).调用此方法会驱动所有进一步的执行.构成主方法的Java虚拟机指令的执行可以导致附加类和接口的链接(并因此创建),以及其他方法的调用.
阅读此链接