jvm类加载机制

类从被加载到虚拟机内存中开始,到卸载出内存为止,分7个部分,其中验证、准备、解析三个阶段统称为连接,如下图(tomcat也有对应的类加载机制,这里先不说了。)

 1)加载:通过ClassLoader获取这个类的二进制字节流,将这个字节流中的数据存储到方法区中,并在内存中生成一个代表这个类的java.lang.Class对象(简称类对象),用来访问方法区这些数据(HotSpot 是把 Class 对象放在方法区中)。【二进制字节流可以从.class文件获取,也可以从ZIP包中读取(比如从jar包和war包中读取),也可以在运行时计算生成(动态代理),也可以由其它文件生成(比如将JSP文件转换成对应的Class类)】。

2)验证:确保Class文件的字节流中包含的信息是否符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。

3)准备:为类的静态变量(常量除外)分配内存并设为默认值(如static int a=123 此时a值为0,在初始化阶段才会变成123),这些内存都将在方法区中进行分配。这一阶段不分配类中的实例变量的内存,实例变量将会在对象实例化时随着对象一起分配在 Java 堆中。

4)解析:将class 常量池 内的 符号引用,加载到 运行时常量池 内成为 直接引用 的过程。符号引用是以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可,但引用目标并不一定已经加载到内存中;直接引用在不同的虚拟机中有不同的实现方式,它可以是 直接指向目标的指针相对偏移量 或是 一个能间接定位到目标的句柄,引用的目标必定已经在内存中(类变量、类方法的直接引用可能是直接指针或句柄,实例变量、实例方法的直接引用都是偏移量。实例变量的直接引用可能是从对象的映像开始算起到这个实例变量位置的偏移量,实例方法的直接引用可能是方法表的偏移量)。

5)初始化:

初始化,为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始化。在Java中对类变量进行初始值设定有两种方式:

  ①声明类变量时指定初始值

  ②使用静态代码块为类变量指定初始值

类的“主动引用”(一定发生初始化)

  • 创建类的实例(如通过new、反射、克隆、反序列化)
  • 访问类的静态变量(除了常量)和静态方法
  • 利用反射调用方法时
  • 初始化类时发现其父类未初始化,则初始化其父类
  • 虚拟机启动时,包含main()方法的类

类的“被动引用”(一定不发生初始化)

  • 访问一个静态变量,但这个变量属于其父类,只会初始化其父类。
  • 创建类的数组不会发生初始化 ( A[] a = new A[10] )。
  • 引用常量不会发生初始化(常量在编译阶段就存入所属类的常量池中了)。

接口的加载过程与类的加载过程稍有不同,接口中不能使用static{}快。当一个接口在初始化时,并不要求其父接口全部都完成初始化,只有在真正用到父接口时(如引用接口中定义的变量)才会初始化。

6)使用:即实例化对象。

原文地址:https://www.cnblogs.com/hongchengshise/p/10458332.html