JVM-类的加载的机制和流程

Posted it江湖之旅

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM-类的加载的机制和流程相关的知识,希望对你有一定的参考价值。

前言

昨天咱们把类的字节码读了一下,那么拿到字节码,咱们的jvm是如何去加载它的呢?这个是今天咱们主要分享的内容。

1.什么是类的加载机制以及他要做哪几件事情

  JVM把class文件加载到内存里面,并对数据进行校验、准备、解析和初始化,最终能够被形成被JVM可以直接使用的Java类型的过程。

JVM-类的加载的机制和流程

1:加载:查找并加载类文件的二进制数据
2:连接:就是将已经读入内存的类的二进制数据合并到JVM运行时环境中去
(1)验证:确保被加载类的正确性,符合JVM规范与安全【主要是验证类的字节码的结构,这个咱们上一篇的文章已经提到了】
(2)准备:为类的 静态变量 分配内存,并初始化它们,例如static int a=3,在此阶段会a被初始化为0
(3)解析:把常量池中的符号引用转换成直接引用
3:初始化:为类的静态变量赋初始值 【这一步才是真正的进行赋值操作】

看一段代码咱们加深一下理解(大家可以根据类的加载过程想想代码的执行结果):[提示:代码看起来不方便,可以在最下面点击阅读原文哈]

public class ClassLoadTest {        /*随着类的加载,验证通过后,进行准备动作,就是分配内存          第3步在进行初始化给变量赋值,这个时候要new 对象了,那么new对象          一定会调用构造方法,于是a++,a变成1,b变成1        */
	private static ClassLoadTest clt = new ClassLoadTest();        /*上面的步骤a为1了,但是这一步又将0赋值给了a        */
	private static int a=0;//准备动作初始化为0
        //b没有赋值,延用之前的值就可以了
	private static int b;//准备动作初始化为0
	private ClassLoadTest(){
		a++;
		b++;
	}	
       public static ClassLoadTest getInstance(){
              return clt; }
       public static void main(String[] args) { ClassLoadTest.getInstance(); System.out.println("a="+a+","+"b="+b); } }

 

其实类的加载主要完成了3件事情:

1:通过类的全限定名来获取该类的二进制字节流
2:把二进制字节流转化为方法区的运行时数据结构
3:在上创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。
类加载的最终产物就是在堆中的class对象;Class对象封装了类在方法区内的数据结构,并向外提供了访问方法区内数据结构的接口

2.类的加载流程和加载时机

 类的加载时机

    当应用程序启动的时候,所有的类会被一次性加载吗?估计你早已知道答案,当然不能,因为如果一次性加载,内存资源有限,可能会影响应用程序的正常运行。那类什么时候被加载呢?例如,A a=new A(),一个类真正被加载的时机是在创建对象的时候,才会去执行以上过程,加载类。当我们测试的时候,最先加载拥有main方法的主线程所在类。

  类的初始化时机

主动引用(发生类初始化过程)
    new一个对象。
    调用类的静态成员(除了final常量)和静态方法。
    通过反射对类进行调用。
    虚拟机启动,main方法所在类被提前初始化。
    初始化一个类,如果其父类没有初始化,则先初始化父类。
被动引用(不会发生类的初始化)    当访问一个静态变量时,只有真正声明这个变量的类才会初始化。
     (子类调用父类的静态变量,只有父类初始化,子类不初始化)。    通过数组定义类引用,不会触发此类的初始化。                 final变量不会触发此类的初始化,因为在编译阶段就存储在常量池中。

再来看一段代码加深一下映像:

[提示:代码看起来不方便,可以在最下面点击阅读原文哈]

public class ClassLoaderProduce {    
       static int d=3;    
       static{          System.out.println("我是ClassLoaderProduce类");         }    
       public static void main(String [] args){            int b=0;        String c="hello";        SimpleClass simpleClass=new SimpleClass();        simpleClass.run();    } }
  class SimpleClass{    
      static int  a=3;    
      static{        a=100;        System.out.println(a);     }    
      public SimpleClass(){        System.out.println("对类进行加载!");     }    
      public void run(){        System.out.println("我要跑跑跑!");     } }

步骤一:因为main方法启动,所以装载ClassLoaderProduce类,在方法区生成动态数据结构(静态变量、静态方法、常量池、类代码),并且在堆中生成java.lang.Class对象;然后进行链接

步骤二:初始化:把static{}与静态变量合并存放在类构造器当中,对静态变量赋值。 1-5行执行完毕。

3.JVM的类加载器

jvm为了保证代码的安全性,有自己的一套类加载器,来保证类的加载,其实也是大家比较熟悉的类加载机制的双亲委派模型。


以上是关于JVM-类的加载的机制和流程的主要内容,如果未能解决你的问题,请参考以下文章

浅谈JVM-图解类加载机制

[JVM] JVM类加载机制总结

类加载机制

深入理解类加载机制

(二十六)类加载机制和类的加载连接(验证准备解析)和初始化

JVM对象创建与内存分配机制