java虚拟机-类加载

1.类加载机制

a. 类从加载到虚拟机到,卸载出内存为止,整个生命周期包括:

加载 -》连接 -》初始化 -》使用 -》 卸载

连接:验证 -》 准备 -》解析

b.什么时候开始类的加载第一阶段 – 加载?

jvm 没有明确的规定强制约束

c. 初始化阶段,五种情况
  1. 遇到new , 读取,设置静态字段(被final 修饰、已经在编译器把结果放到常量池的静态地段除外)
  2. 通过 反射手段 实例化一个类,发现该类没有被初始化时
  3. 调用一个类,发现该类的父类没有初始化时
  4. jvm启动时,需要 加载一个主类,虚拟机先执行该主类

以上几种情况是对一个类的主动引用,如果是被动引用则不会触发初始化

d.被动引用的情况

1.通过子类引用父类的静态字段,不会导致子类的初始化

public class Main {

    public static void main(String[] args) {

        System.out.println(Sub.x);
        /*
         *  parent init !
            10
         * 
         *      不会初始化子类 sbu ,因为该静态字段是在父类中定义的
         */
    }
}

class Parent {

    static {
        System.out.println("parent init !");
    }
    public static int x = 10;

}
class Sub extends Parent{

    static {
        System.out.println("sub init !");
    }
}

2.通过数组的定义来引用类,不会触发该类的初始化

public static void main(String[] args) {

//      System.out.println(Sub.x);

        Parent[] parents = new Parent[10];

        /*
         * 不会输出任何信息 
         */ 
}

3.常量在编译器期存入调用类的常量池中,本质上没有被直接引用定义该常量的类,因此不会触发定义类的初始化

public class Main {

    public static void main(String[] args) {

        System.out.println(Sub.y);      // 访问 Sub.y 不会触发父类的 和 子类的初始化 
    }   
}

class Parent {

    static {
        System.out.println("parent init !");
    }
    public static int x = 10;

    public  static final int y = 1;

}

2.类加载过程

a.加载

加载和类加载时两个概念,加载时类加载的第一个阶段

在加载阶段,,需要完成三件事情

1.通过一个全限定类名区获取定义此类的二进制流 (可以来自网络,计算机生成,文件生成,zip包)

2.将这个字节流所代表的静态存储结果转换 为方法区的动态数据结构

3.在内存中生成一个代表该类的java.lang.Class(字节码)对象,作为方法区这个类的各种数据的入口

b.验证

目的:确保 Class 文件的字节流包含的信息符合当前虚拟机的要求,并且不会危害虚拟机

1.文件格式验证

字节流是否符合 Class 文件的规范,并且能被当前虚拟机处理

  • 验证魔数
  • 主版本、此版本号
  • 常量池中的数据是否 有不被支持的类型

2.元数据验证

保证符合java语言规范的要求

  • 这个类是否有父类(所有的类都有Object 这个父类)
  • 这个类是否继承了不允许被继承的类
  • 如果这个类是抽象类,是否实现了父类,或接口中所有 被要求实现的方法
3.字节码验证

对类的方法体进行校验分析,保证校验类的方法在运行时不会做出危害虚拟机的安全事件

c.准备阶段

正式为类变量分配内存并设置类变量的初始值阶段。值得注意的是,这里的变量使用的内存是在方法区中分配的

进行分配内存的只有类变量(被 static修饰)不包括实例变量,实例变量会在对实例化的时候随对象分配到 java堆中

这里的实例化并不是赋值为指定的值,而是变量类型的零值

public static int x = 123;
// 准备阶段过后,x = 0
// 初始化阶段后 x = 123
d.解析

解析是虚拟机将常量符号引用转换成直接引用的过程

e.初始化

在准备阶段,变量已经被赋上“零”值,而在这个阶段,将程序员指定的值进行赋值

初始化过程是执行类构造器的 方法的过程

  • () 方法时由编译器自动收集类的所有类变量的赋值的赋值操作,和静态语句块中的的语句合并产生
  • 编译器收集的顺序是语句在文件出现的顺序决定的
  • 静态语句块只能访问到定义在静态语句块之前的变量
  • 定义在静态语句块之后的变量,前面的静态语句块能给其赋值,但是不能访问

  • 静态字段静态代码块顺序执行

  • 在执行子类初始化之前先执行父类的初始化
class Parent {

    static {

        x = 10;             // 可以给定义在 static{}后的变量赋值

        System.out.println(x);      // 不可以访问 
    }
    public static int x = 10;

}

3.类加载器

3.1类与类加载器

程序员可以自定义加载器

  • 比较两个雷是否“相等”,只有是由同一个类加载器加载的前提下才有意义
  • 否则,即使这两个类来自同一个 Class 文件,被同一个虚拟机加载,但是加载他们的类加载器不同,这两个类就不相等

这里的“相等”,包括代表类的 Class 对象的 equals()方法和 isInstance()方法

如果来自不同的类加载器, equalas() 和 isInstance()都不同

3.2双亲委派机制

类加载器:

  • 启动类加载器

    加载\lib 目录下的

  • 扩展类加载器

    加载\lib\ext 目录下的

  • 应用程序类记载器

    如果应用程序没有自定义类加载器,就是程序中默认的加载器

双亲委派机制工作:如果一个类加载器收到了类加载的请求,不会自己去尝试加载这个类,而是把这请求委派给其父类加载器去完成。每一层的加载器都是 如此,因此所有的加载请求都会被传到顶层的启动类加载器中,只有当其父类反馈自己无法完成加载这个请求时,子类加载器才会去尝试自己去完成加载

因此Object 类的程序在各种加载器环境中都是同一个类

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值