JVM启动过程包括:加载、连接、初始化
1.加载:就是将class文件加载到内存。详细的说是,将class文件加载到运行时数据区的方法区内(JDK7是方法区,JDK8对应的是Metaspace),然后创建一个java.lang.Class对象,用来封装类在方法区类的数据结构。JVM规范并未说明Class对象位于何处,Hotspot虚拟机将其放在了方法区
有两种类型的类加载器:
1.1JVM自带提供的三类加载器:根类加载器Bootstrap Classloader(C++写的, 程序员无法在JAVA代码中获得该类)
扩展加载器Extension Classloader,使用Java代码实现
系统加载器System ClassLoader,也叫应用加载器 Application Classloader,使用Java代码实现
1.2用户自定义的类加载器,都是java.lang.ClassLoader的子类
2.连接分为三步 2.1.验证:检查被加载的类的正确性;
2.2.初始化(或者叫准备更好):接着将静态变量初始化为默认值,比如一个类中有一句private static int num=1; 实际上在这一步的时候,只是将num初始化为默认值0
2.3.解析:把类中的符号引用转换为直接引用,这个其实也好理解,毕竟所有类都加载好,才能真正进行直接引用,但是类的加载也有现后顺序之分,所以如果先加载的类引用了后加载的类,只有等到后者完成加载,才能真正引用到内存中的地址
3. 初始化。"任何JVM实现,必须在每个类或接口被JAVA程序"首次主动使用时"才初始化它们",这一步将静态变量最终赋值,比如上面举例钟,num变量将被赋值为1。静态代码块也在这一步被执行
例子:下面这个程序,的执行过程:
package com.test.jvm.common; class Singleton { private static Singleton singleton = new Singleton(); public static int num1; public static int num2=0; public Singleton() { num1++; num2++; } public static Singleton getSingleton() { return singleton; } } public class Main { public static void main(String[] args) { Singleton singleton = Singleton.getSingleton(); System.out.println(Singleton.num1); System.out.println(Singleton.num2); } }
首先加载class类,因为class类主动使用了Singleton类的方法,于是Singleton类也开始加载,加载完毕后Singleton类的static变量开始赋予默认值,注意是默认值而不是初始值,于是singleton被赋值null,
num1和num2均赋值为0,然后开始初始化,从上到下的执行顺序,singleton被初始化为new Singleton(),这导致构造函数的调用,于是num1和num2都被赋值为1,singleton初始化完成之后,num1接着初始化,可是num1没有初始值,于是num1保持为1不变,然后轮到num2初始化,num2被初始化为0。后面就略了,所以最后执行结果是1和0
附类的初始化时机:
======================================================================================================================================================
package com.test.jvm.common; /** * 学习类的加载过程 当静态变量为final的时候 */ class FinalTest { public static final int x = 6/3; static { System.out.println("FinalTset static block"); } } public class Main { public static void main(String[] args) { System.out.println(FinalTest.x); } }
执行结果为2,并没有输出那句话,这是因为FinalTest1并没有得到初始化,没有得到初始化的原因是6/3是编译时常量
package com.test.jvm.common; import java.util.Random; /** * 学习类的加载过程 当静态变量为final的时候 */ class TestClass2 { public static final int x = new Random().nextInt(100);//0~99 static { System.out.println("FinalTset2 static block"); } } public class FianlTest2 { public static void main(String[] args) { System.out.println(TestClass2.x); } }
结果:
FinalTset2 static block
84
这是因为FinalTest2得到了初始化,原因是 new Random().nextInt(100); 是运行时才能确定的量
=====================================================================================
package com.test.jvm.common; class Parent3 { static int a = 3; static { System.out.println("Parent3 static block"); } } class Child3 extends Parent3 { static { System.out.println("Child3 static block"); } } /** * 只有当程序访问的静态变量或静态方法确实在当前类或者当前接口中定义时,才可以认为是对类或接口的主动使用 */ public class StaticTest3 { public static void main(String[] args) { System.out.println(Child3.a); } }
运行结果:
Parent3 static block
3
====================================================================================
package com.test.jvm.common; class CL { static { System.out.println("Class CL"); } } /** * 调用ClassLoader类的loadClass方法加载一个类,并不是对类的主动使用,不会导致类的初始化 */ public class StaticTest4 { public static void main(String[] args) throws ClassNotFoundException { ClassLoader loader = ClassLoader.getSystemClassLoader(); Class<?> clazz = loader.loadClass("com.test.jvm.common.CL"); System.out.println("-----------------------"); clazz = Class.forName("com.test.jvm.common.CL"); } }
运行结果:
----------------------- Class CL