前面的运行时数据区如下图:
JVM用来存储加载的类信息、常量、静态变量、编译后的代码等数据虚拟机规范中这是一个逻辑区划,具体实现根据不同的虚拟机来实现,但是.class文件是怎么加载进去的,说到这个就要看一下类的生命周期
在java中有专门负责加载工作的类加载器:类加载器负责装入类,搜索网络、jar、zip、文件夹、二进制数据、内存等制定位置的类资源。一个java程序运行,最少有三个类加载器实例,负责不同类的加载,分别是:
- Bootstrap loader核心类库加载器:C/C++实现,无对应java类:null加载JRE_HOME/jre/lib目录,或用户配置的目录,JDK的核心类库rt.jar...String...
- Extension Class Loader拓展类库加载器:ExtClassLoader的实例:加载JRE_HOME/jre/lib/ext目录,JDK拓展包,或用户配置的目录。
- application class loader用户应用程序加载器:APPClassLoader的实例:加载java.calss.path指定的目录,用户应用程序class-path或者java运行时参数-cp
通过JDK-API进行查看:java.lang.Class.getClassLoader(),返回装载类的类加载器,如果这个类是由bootstrapClassLoader加载的,那么这个方法在这种实现中将返回null。
类不会重复加载:类的唯一性:同一个类加载器,类名一样代表是同一个类。
识别方法:ClassLoader Instance id + Pagename + ClassName
验证方式:使用类加载器,对同一个class类的不同版本,进行多次加载,检查是否会加载到最新的代码
JVM如何知道我们的类在何方呢?
class信息存放在不同位置,桌面jar、项目bin目录、target目录等等。。
查看openjdk源代码:sun.misc.Launcher.appClassLoader
结论:读取java.class.path配置,指定去那些地址加载类资源
验证过程:利用jps、jcmd两个命令
- jps查看本机Java进程
- 查看运行时配置:jcmd 进程号 VM.system_properties
类的卸载需要满足以下条件:
- 该class所有的实例已经被GC;
- 加载该类的ClassLoader实例已经被GC
验证方法:jvm启动中增加-verbose:class参数,输出类加载和卸载的日志信息
静态代码块和静态变量会在类第一次创建时被执行
双亲委派模型:
为了避免重复加载 ,由下到上逐级委托,由上到下逐级查找。
首先不会自己去尝试加载类,而是把这个请求委派给父加载器去完成,每一层次的加载器都是如此,因此所有的类请求都会上传给上层的启动类加载器,只有当父类加载器反馈自己无法完成该加载请求(该加载器的搜索范围中没有找到对应的类)时,子加载器才会尝试自己去加载。
注:类加载器之间不存在父类子类的关系,“双亲”是翻译,可以理解为逻辑上定义的上下级关系。
通过创建新的类加载器,可以达到热加载的效果