讲JVM之前,先回顾下类加载器
类加载器一共有四类,见如下图
我们都知道加载类需要类加载器,那么选用哪个类加载器呢?
JVM利用双亲委派机制,加载类时依次向上获取父类,即向上委托父类,直到Bootstarp ClassLoader,如果父类加载器为null,则判断当前类加载器是否为null,不是null则为真正的类加载器。可以理解为就远原则,但要注意自身是否为null。
如何自主加载类
有两种方式:1.Class.forName("packageName+className");
2.ClassLoader.loadClass("packageName+className");
区别:第一汇总方式不仅将类加载到jvm,还会初始化静态变量
接着说到类的生命周期。
在加载类之前,类文件被编译成字节码(.class文件),由jvm解释执行程序。所以jvm加载类,实际上是加载字节码的过程。
从jvm加载类到初始化类大致分为3步
加载类-->连接-->初始化
其中连接阶段,又分为验证、准备和解析,主要为变量或对象分配内存空间和初始化。
验证:hotspot(sun公司jvm的版本)验证类是否会对自身造成危害,如果不会,则将类加载进来,否则不加载。
准备:给static变量或常量分配内存,并赋予默认值。比如static int a = 100;
a赋值为0,
static String str = "abc";
str赋值为null
特殊情况,static final修饰的常量在准备阶段不仅进行赋值,还初始化。例如:
static final int b = 200;
此阶段b的值为200,而非0。
解析:将字节码中的符号引用转换为直接引用。
备注:
类初始化顺序,由上至下依次执行:
静态变量、静态代码块(视代码顺序)(从类加载开始,初始化只执行一次)
非静态代码块
构造函数
初始化:为类的属性(包括静态变量)赋值以及执行构造函数的过程(有先后顺序,视代码顺序定)。比如常见的new对象,在jvm中,类的初始化只会主动进行,真正开始执行java代码,而不会被动进行,因此能通过五中常见的主动方式判断类是否进行了初始化,被动方式是不会触发类的初始化的。
附从网络上搬运的5种主动出初始化方式:
1、使用new字节码指令创建类的实例,或者使用getstatic、putstatic读取或设置一个静态字段的值(放入常量池中的常量除外),或者调用一个静态方法的时候,对应类必须进行过初始化。
2、通过java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则要首先进行初始化。
3、当初始化一个类的时候,如果发现其父类没有进行过初始化,则首先触发父类初始化。
4、当虚拟机启动时,用户需要指定一个主类(包含main()方法的类),虚拟机会首先初始化这个类。
5、使用jdk1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、RE_invokeStatic的方法句柄,并且这个方法句柄对应的类没有进行初始化,则需要先触发其初始化。
下面说下被动初始化的几种方式:
1.引用父类的静态变量字段;
2.定义数组对象;
3.引用类的常量(包括static final修饰的常量)