JVM 类加载机制详解

一 类加载机制

JVM类加载分为5个过程:加载,验证,准备,解析,初始化,使用,卸载,如下图所示: 

这里写图片描述
下面来看看加载,验证,准备,解析,初始化这5个过程的具体动作。

1.1 加载

加载主要是将.class文件(并不一定是.class。可以是ZIP包,网络中获取)中的二进制字节流读入到JVM中。 
在加载阶段,JVM需要完成3件事: 
1)通过类的全限定名获取该类的二进制字节流; 
2)将字节流所代表的静态存储结构转化为方法区的运行时数据结构; 
3)在内存中生成一个该类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。

1.2 连接

1.2.1 验证

验证是连接阶段的第一步,主要确保加载进来的字节流符合JVM规范。 
验证阶段会完成以下4个阶段的检验动作: 
1)文件格式验证 
2)元数据验证(是否符合Java语言规范) 
3)字节码验证(确定程序语义合法,符合逻辑) 
4)符号引用验证(确保下一步的解析能正常执行)

1.2.2 准备

准备是连接阶段的第二步,主要为静态变量在方法区分配内存,并设置默认初始值。

1.2.3 解析

解析阶段是指虚拟机将常量池中的符号引用替换为直接引用的过程。符号引用就是class文件中的:

  • CONSTANT_Class_info
  • CONSTANT_Field_info
  • CONSTANT_Method_info

等类型的常量。

下面我们解释一下符号引用和直接引用的概念:

  • 符号引用与虚拟机实现的布局无关,引用的目标并不一定要已经加载到内存中。各种虚拟机实现的内存布局可以各不相同,但是它们能接受的符号引用必须是一致的,因为符号引用的字面量形式明确定义在Java虚拟机规范的Class文件格式中。
  • 直接引用可以是指向目标的指针,相对偏移量或是一个能间接定位到目标的句柄。如果有了直接引用,那引用的目标必定已经在内存中存在。

1.3 初始化

初始化阶段是类加载过程的最后一步,主要是根据程序中的赋值语句主动为类变量赋值。 
注: 
1)当有父类且父类为初始化的时候,先去初始化父类; 
2)再进行子类初始化语句。

什么时候需要对类进行初始化? 
1)使用new该类实例化对象的时候; 
2)读取或设置类静态字段的时候(但被final修饰的字段,在编译器时就被放入常量池的静态字段除外static final); 
3)调用类静态方法的时候; 
4)使用反射Class.forName(“xxxx”)对类进行反射调用的时候,该类需要初始化; 
5) 初始化一个类的时候,有父类,先初始化父类(注:1. 接口除外,父接口在调用的时候才会被初始化;2.子类引用父类静态字段,只会引发父类初始化); 
6) 被标明为启动类的类(即包含main()方法的类)要初始化; 
7)当使用JDK1.7的动态语言支持时,如果一个java.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。

以上情况称为对一个类进行主动引用,且有且只要以上几种情况需要对类进行初始化。

分析以下题目:

public class ClassLoaderTest {
    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        System.out.println("Singleton1 value1:" + singleton.value1);
        System.out.println("Singleton1 value2:" + singleton.value2);

        Singleton2 singleton2 = Singleton2.getInstance2();
        System.out.println("Singleton2 value1:" + singleton2.value1);
        System.out.println("Singleton2 value2:" + singleton2.value2);
    }
}

class Singleton{
    private static Singleton singleton = new Singleton();
    public static int value1;
    public static int value2 = 0;

    private Singleton(){
        value1++;
        value2++;
        System.out.println(value1+":"+value2);  //这里输出1:1
    }

    public static Singleton getInstance(){
        return singleton;
    }
}

class Singleton2{
    public static int value1;
    public static int value2 = 0;
    private static Singleton2 singleton2 = new Singleton2();

    private Singleton2(){
        value1++;
        value2++;
    }

    public static Singleton2 getInstance2(){
        return singleton2;
    }
}

 
Singleton输出结果:1  0 
 原因:
  1 首先执行main中的Singleton singleton = Singleton.getInstance(); 
  2 类的加载:加载类Singleton 
  3 类的验证 
  4 类的准备:为静态变量分配内存,设置默认值,即singleton(引用类型)设为null,value1,value2(基本数据类型)设置默认值0 
  5 类的初始化(按照赋值语句进行修改): 
   执行private static Singleton singleton = new Singleton(); 
   执行Singleton的构造器:value1++;value2++;  此时value1,value2均等于1 
   执行 
   public static int value1; 
   public static int value2 = 0; 
   此时value1=1,value2=0


  Singleton2输出结果:1  1 
 原因:
  1 首先执行main中的Singleton2 singleton2 = Singleton2.getInstance2(); 
  2 类的加载:加载类Singleton2 
  3 类的验证 
  4 类的准备:为静态变量分配内存,设置默认值,即value1,value2(基本数据类型)设默认值0,singleton2(引用类型)设为null
  5 类的初始化(按照赋值语句进行修改): 
  执行 
  public static int value2 = 0; 
  此时value2=0(value1不变,依然是0); 
  执行 
  private static Singleton singleton = new Singleton(); 
  执行Singleton2的构造器:value1++;value2++;  
  此时value1,value2均等于1,即为最后结果

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值