JVM-----类加载(类只需要加载一次就可以,不需要反复加载)

一、

1、类从加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:

加载-验证-准备-解析-初始化-使用-卸载,其中验证-准备-解析称为链接。

二、

2、类加载阶段虚拟机需要完成以下事情:

(1)将class文件字节码内容加载到内存中。

(2)将这些静态数据结构转换为方法区运行时数据结构。

(3)在堆中生成一个代表这个类的Class对象,作为方法区类数据的访问入口。

3、链接阶段

将java类的二进制代码合并到JVM运行状态之中(即将静态的字节码数据转换为可运行的数据)。

验证:确保加载的类信息符合JVM规范,没有安全方面的问题。

准备:正式为类变量(static变量)分配内存并设置类变量初始化的值,这些内存都将在方法区中进行分配。

解析:将虚拟机常量池中的符号引用替换为直接引用的过程。(注:符号引用是通过一组符号描述目标,而直接引用是通过指针直接或间接的定位到目标)

4、初始化阶段

(1)初始化阶段是执行类构造器<clinit>()方法的过程,类构造器<clinit>()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static块)中的语句合并产生的。

(2)当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。

(3)虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步。

(4)当虚拟机启动时,用户需要制定一个要执行的主类,(有main方法的那个类),虚拟机会先初始化这个类。

(5)当访问一个java类的静态域时,只有真正声明这个域的类才会被初始化。

三、

5、一定会发生类的初始化(加载)事件(类的主动引用)

(1)new一个类的对象。

(2)使用类的静态成员(除了final常量)和静态方法。

(3)使用java.lang.reflect包的方法对类进行反射调用(例如:Class.forName("com.shxt.A").

   (4)虚拟机启动,java Hello,则一定会初始化Hello类,说白了就是先启动main方法所在类。

(5)当初始化一个类,如果其父类没有被初始化,则会先初始化它的父类。

注:类的被动引用,不会发生类的初始化(加载)

(1)通过数组定义类引用,不会发生此类的初始化(例如A [] arr = new A[10]).

(2) 使用类的常量不会触发此类的初始化(常量在编译阶段就存入常量池中了)

(3)当访问一个静态变量时,只有真正声明这个变量的类才会被初始化。

例如:

public static void main(String [] args){

    System.out.println(B.width);

}

class B extends A{

}

class A {

    public static int width=100;

}

此时,A会被初始化,但B不会被初始化。

四、

6、类加载器

从java虚拟机的角度来说,只存在两种不同的类加载器:一种是启动类加载器,这个类加载器使用c++实现,是虚拟机自身的一部分;另一种就是所有其他的类加载器,这些类加载器都是由java实现,且全部继承自java.lang.ClassLoader。

加载器的关系如下:


(1)启动类加载器

用来加载java的核心库(加载JAVA_HOME/jre/lib/rt.jar中的类),启动类加载器无法被java程序直接使用。

(2)扩展类加载器

负责加载java的扩展库(加载JAVA_HOME/jre/ext/*.jar中的类),开发者可以直接使用扩展类加载器。

(3)应用程序类加载器(加载classpath中的类),开发者可以直接使用这个类加载器,若应用程序中没有定义过自己的类加载器,java应用的类都是由它来完成加载的。

(4)自定义类加载器

开发人员可以通过继承java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求。

7、双亲委托机制(是代理模式的一种,代理模式:交给其他加载器来加载指定的类)

若一个类加载器收到了类加载请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,依次追溯,直到最高的爷爷辈的,如果父类加载器可以完成委托任务,就成功返回;只有父类加载器无法完成加载任务时,才自己去加载。


双亲委托机制作用:保证java核心库的类型安全。

这用机制就保证不会出现用户自己能定义java.lang.Object类的情况。

即类加载器除了用于加载类,也是安全的最基本的屏障。


注:并不是所有的类加载器都采用双亲委托机制。

tomcat服务器类加载器也使用代理模式,所不同的是它首先尝试自己去加载这个类,如果找不到在代理给父类加载器,这与一般的类加载器的顺序是相反的。


五、

7、比较两个类是否相同

比较两个类是否相同,只有这两个类是由同一个类加载器加载的前提下才有意义,否则即使这两个类来自同一个class文件,只要加载它们的加载器不同,它们就是不同的类。









猜你喜欢

转载自blog.csdn.net/g1607058603/article/details/80698449