JVM类加载机制讲解
Posted 栗子~~
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM类加载机制讲解相关的知识,希望对你有一定的参考价值。
文章目录
前言
如果您觉得有用的话,记得给博主点个赞,评论,收藏一键三连啊,写作不易啊^ _ ^。
而且听说点赞的人每天的运气都不会太差,实在白嫖的话,那欢迎常来啊!!!
JVM类加载机制讲解
一、什么是JVM类加载机制?
概述:java JVM 把【描述类的数据】从class文件中加载到内存,并对该数据进行验证,转换解析和初始化,最终形成可以
【被虚拟机直接使用的java类型】,这个过程我们成为【JVM的类加载机制】。
二、类加载的时机
类【加载】的五个阶段:
加载、验证、准备、解析、初始化
类从【加载】到【卸载】的整个生命周期:
加载、验证、准备、解析、初始化、使用、卸载,其中验证、准备、解析可以把他们统称为连接:
即生命整个生命周期:
【加载】 --> 【连接】 -->【初始化】 --> 【使用】 --> 【卸载】
三、类加载的五个阶段
1、加载:
1)通过一个类的全限定定名来获取【定义此类的二进制字节流】:。
2)将这个字节流中所代表的【静态存储结构】:转化为【方法区中的运行时数据结构】:。
3)在内存中生成一个【代表这个类的 java.lang.Class 对象】:,作为方法区这个类的各种数据的入口。
2、验证:
确保 Class 文件的字节流中包含的信息是否符合【当前虚拟机】:的要求,保证这些信息被当做代码运行后,不会对JVM造成自身安全。
3、准备:
在方法区中分配这些变量所使用的内存空间。
4、解析:
虚拟机将常量池中的符号引用替换为直接引用的过程。
5、初始化:
执行类中定义的 Java 程序代码。
注::初始化阶段,主要通过类构造器来实现,即初始化阶段是执行类构造器方法的过程:。
四、什么类型的数据不会初始化? ------- 论点
结论:
所有【引用类型】外加【常量】的方式都不会触发初始化,我们称之为【被动引用】。
论证前准备:
什么是引用类型,什么是基础类型:
在Java虚拟机中,数据类型可以分为两类:基本类型和引用类型。基本类型的变量保存原始值,即:他代表的值就是数值本身;而引用类型的变量保存引用值。“引用值”代表了某个对象的引用,而不是对象本身,对象本身存放在这个引用值所表示的地址的位置。
【基础类型】:byte,short,int,long,char,float,double,Boolean
【引用类型】:类类型,接口类型和数组。
论证:
例子1:使用类字段
原因:通过子类引用父类的静态字段,不会导致子类初始化,对于静态字段,只有直接定于这个字段的类才会初始化。
public class SuperClass
static
System.out.println("This is SuperClass !!!");
public static int index = 55555;
public class NodeClass extends SuperClass
static
System.out.println("This is NodeClass");
使用:
public static void main(String[] args)
System.out.println(NodeClass.index);
效果:父类初始化,子类并没有初始化
例子2:通过数组定义来引用类,不会触发此类的初始化
原因:数组是引用类型
正常初始化:
public static void main(String[] args)
NodeClass test = new NodeClass();
效果:正常初始化
用数组初始化:
public static void main(String[] args)
NodeClass[] superClass = new NodeClass[10];
效果:通过数组定义引用类,不会触发此类的初始化
例子3:常量测试,不会触发定义常量所在类的初始化
原因:在源码阶段,确实引用常量类ConstClass中的HELLO_WORLD,但实际上在编译阶段通过常量传播优化,
已经将此常量的值"hello world !!!" 直接存在ConstClass类的常量池中,因此ConstClass类对HELLO_WORLD中的引用,
其实都转化成类对自身【常量池】的引用了。
public class ConstClass
static
System.out.println("This is ConseClass");
public static final String HELLO_WORLD = "hello world !!!";
public static String index = "Hello World !!!";
类调非常量:
public static void main(String[] args)
System.out.println(ConstClass.index);
效果:正常初始化
类调常量:
public static void main(String[] args)
System.out.println(ConstClass.HELLO_WORLD);
效果:不会触发定义常量所在类的初始化
五、类加载器
1、【类加载器】用处:
对于任何一个类,都必须由【加载它的类加载器】和【类本身】一起共同确认其在java JVM中的【唯一性】
2、【类加载器】使用原则:
类加载器是在双亲委派模型的基础上实现的。
3、【类加载器】特性:
每个类加载器,都拥有一个【独立的类名称空间】,即如果比较两个类是否相等,只有在这两个类在都是由同一个类加载器加载而来的前提下,才有意义。
六、JVM提供了三种类加载器,分别为:
1)启动类加载器(Bootstrap ClassLoader)
负责加载 JAVA_HOME\\lib 目录中的,或通过-Xbootclasspath 参数指定路径中的,且被
虚拟机认可(按文件名识别,如 rt.jar)的类。
2)扩展类加载器(Extension ClassLoader)
负责加载 JAVA_HOME\\lib\\ext 目录中的,或通过 java.ext.dirs 系统变量指定路径中的类
库。
3)应用程序类加载器(Application ClassLoader):
负责加载用户路径(classpath)上的类库。
JVM 通过双亲委派模型进行类的加载,当然我们也可以通过继承 java.lang.ClassLoader 实现自定义的类加载器。
七、双亲委派
双亲委派模型:
概述:
当一个类收到了类加载器的请求时,他首先不会尝试自己去加载这个类,而是把这个请求委派给【父类加载器】去完成,每一个层次的【类加载器】都是如此,因此所有的加载请求都应该传送到最顶层的【启动类加载器】其中, 只有当【当父类加载器】反馈自己无法完成这个请求的时候(在它的加载路径下没有找到所需加载的 Class),【子类加载器】才会尝试自己去加载。
采用双亲委派的一个好处是比如加载位于 rt.jar 包中的类 java.lang.Object,不管是哪个加载 器加载这个类,最终都是委托给顶层的启动类加载器进行加载,这样就保证了使用不同的类加载 器最终得到的都是同样一个 Object 对象。
好处:
1)保证java类型体系中最基础的行为
2)双亲委派模型对于保证java程序的稳定运作极为重要
八、打破双亲委派例子
OSGI实现的模块化部署打破双亲委派模型
即在OSGI环境下,类加载器不在是双亲委派模型推荐的【树状结构】,而是进一步发展为更加复杂的【网状结构】。
以上是关于JVM类加载机制讲解的主要内容,如果未能解决你的问题,请参考以下文章