Java的程序要运行须要将编译好的class文件加载到JVM运行时数据区。java
在了解类的加载机制以前,咱们须要了解一下类的生命周期。Java类从被加载到JVM内存开始,到卸载出内存为止,它的整个生命周期包括了:加载(Loading),验证(Verification),准备(Preparation),解析(Resolution),初始化(Initialization),使用(Using)和卸载(Unloading)七个阶段。安全
Java类的加载须要用到类加载器。类加载器负责装入类,搜索网络,jar,zip,文件夹,二进制数据,内存等指定位置的资源。一个Java程序运行,至少有3个不一样的类加载器实例,负责加载不一样的类。这三个类加载器分别为,启动类加载器(Bootstrap ClassLoader),扩展类加载器(Extension ClassLoader),应用程序类加载器(Application ClassLoader)。markdown
经过JDK提供的API:java.lang.Class.getClassLoader() 能够进行类加载器的查看,该API会返回装载类的类加载器,若是这个类是由Bootstrap ClassLoader加载的,那个这个方法会返回null。网络
代码示例:app
public class ClassLoaderView { public static void main(String[] args) throws Exception { // 加载核心类库的 BootStrap ClassLoader System.out.println( "核心类库加载器:" + ClassLoaderView.class .getClassLoader() .loadClass("java.lang.String") .getClassLoader()); // 加载拓展库的 Extension ClassLoader System.out.println( "拓展类库加载器:" + ClassLoaderView.class .getClassLoader() .loadClass("com.sun.nio.zipfs.ZipCoder") .getClassLoader()); // 加载应用程序的 Application ClassLoader System.out.println("应用程序库加载器:" + ClassLoaderView.class.getClassLoader()); } } //运行结果: 核心类库加载器:null 拓展类库加载器:sun.misc.Launcher$ExtClassLoader@7f31245a 应用程序库加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
class信息能够存在不一样的地方,那么JVM是如何知道咱们的类存在什么地方的哪?经过查看sun.misc.Launcher.AppClassLoader的源码咱们能够看到,它会读取java.class.path这个配置来获取那些地址加载类资源。参考如下代码示例,利用jsp和jcmd两个命令能够进行验证。jvm
代码示例:jsp
public class HelloWord { public static void main(String[] args) throws IOException { System.out.println("Hello Word"); System.in.read(); } }
jsp命令能够查看本机Java进程,jcmd命令能够查看运行时配置:jcmd 进程号 VM.system_propertiesui
Java中的类并不会重复加载,同一类加载器,同一类名,表明的是同一个类。而避免类重复加载的主要缘由在于JVM在加载类时默认采用的是双亲委派模型。所谓的双亲委派模型,就是某个特定的类加载器在接到类的加载请求时,首先将加载任务委托给父加载器,依次递归,若是父加载器能够完成类加载任务,就成功返回;只有父加载器没法完成此加载任务时,才本身去加载。由下到上逐级委托,由上到下逐级查找,双亲委派模型保证了Java核心库的类型安全url
JVM中的类不可能一直存在,在知足必定条件的状况下类会被卸载掉。在知足该Class的全部实例都已被垃圾回收,同时加载该类的ClassLoader实例也已经被垃圾回收,那么这个类会被JVM卸载掉。在JVM启动中增长-verbose:class参数,能够输出类加载和卸载的日志信息。spa