虚拟机类加载

虚拟机如何加载class文件? class文件中信息进入到虚拟机后会发生什么变化?

虚拟机的类加载机制:虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型。

 

Java语言,类型的加载、连接和初始化过程都是在程序运行期间完成的,这种策略虽然会令类加载时稍微增加一些性能开销,但是会为Java应用程序提供高度的灵活性,Java的天生可以动态扩展的语言特性就是依赖运行期动态加载和动态连接这个特点实现的。

 

类加载的整个生命周期-------加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7个阶段

其中:加载、验证、准备、初始化和卸载这5个阶段的顺序是确定的,类的加载过程必须按照这种顺序按部就班地开始(注意:是开始而不是进行,因为这些阶段通常都是互相交叉地混合式进行的,通常会在一个阶段执行的过程中调用、激活另外一个阶段)

而解析阶段在某些情况下可以在初始化阶段之后再开始,这是为了支持Java语言的运行时绑定(也称为动态绑定或晚期绑定)


 

二、类加载全过程

      2、1 加载

             1)通过一个类的全限定名来获取定义此类的二进制字节流

             2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构

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

  2、2验证

  •        文件格式验证   验证字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理
  •        元数据验证   对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言规范的要求
  •        字节码验证   通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的
  •        符号引用验证    对类自身以外(常量池中的各种符号引用)的信息进行匹配性校验

 

2、3准备

       正式为类变量(static变量)分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配

 

2、4解析

    虚拟机将常量池内的符号引用替换为直接引用的过程

 

2、5初始化

对于初始化阶段,是类加载过程的最后一步,到了这个阶段才真正开始执行类中定义的Java程序代码(或者说是字节码)。在准备阶段,变量已经赋过一次系统要求的初始值,而在初始化阶段,则根据程序员通过程序制定的主观计划去初始化类变量和其他资源。 

  • 初始化阶段是执行类构造器<clinit>()方法的过程。类构造器<clinit>()方法是由编译器自动收集
    类中的所有类变量的赋值动作和静态语句块(static块)中的语句合并产生的
  • 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步

虚拟机规范严格规定了在以下情况必须立即对类进行“初始化”

  • 类的主动引用(一定会发生类的初始化)
    • new一个类的对象
    • 调用类的静态成员(除了final常量)和静态方法
    • 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化
    • 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化
    • 当虚拟机启动时,先初始化main方法所在的类
    • 当使用JDK 1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化

 

  • 类的被动引用(不会发生类的初始化)
    • 当访问一个静态域时,只有真正声明这个域的类才会被初始化
      • 通过子类引用父类的静态变量,不会导致子类初始化
    • 通过数组定义类引用,不会触发此类的初始化
    • 引用常量不会触发此类的初始化(常量在编译阶段就存入调用类的常量池中了)

注意:一个接口在初始化时,并不要求其父接口全部都完成了初始化,只有在真正使用到父接口的时候(如引用接口中定义的常量)才会初始化。

 

 

三、类加载器

          类加载器:通过一个类的全限定名来获取描述此类的二进制字节流的代码块

 3、1 类加载器的层次结构

  •      启动类加载器

-它用来加载 Java 的核心库(JAVA_HOME/jre/lib/rt.jar,或sun.boot.class.path路径下的内容),是用原生代码来实现的,并不继承自 java.lang.ClassLoader

-加载扩展类和应用程序类加载器。并指定他们的父类加载器

 

  •     扩展类加载器

-用来加载 Java 的扩展库(JAVA_HOME/jre/ext/*.jar,或java.ext.dirs路径下的内容) 。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java类。
          -由sun.misc.Launcher$ExtClassLoader实现

 

  •    应用程序类加载器

-它根据 Java 应用的类路径(classpath,java.class.path 路径下的内容)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。

-由sun.misc.Launcher$AppClassLoader实现

  •   自定义类加载器

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

 

类加载器之间的这种层次关系,称为类加载器的双亲委派模型(Parents Delegation Model)。双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器。这里类加载器之间的父子关系一般不会以继承(Inheritance)的关系来实现,而是都使用组合(Composition)关系来复用父加载器的代码。

 

双亲委派模型的工作过程:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。

 

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

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

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

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

双亲委派模型的实现代码

protected synchronized Class<?>loadClass(String name,boolean resolve)throws ClassNotFoundException

 {

 //首先,检查请求的类是否已经被加载过了

 Class c=findLoadedClass(name);

 if(c==null){

 try{
//若没有加载则调用父加载器的loadClass()方法

 if(parent!=null){

 c=parent.loadClass(name,false);

 }else{
//若父加载器为空则默认使用启动类加载器作为父加载器

 c=findBootstrapClassOrNull(name);

 }

 }catch(ClassNotFoundException e){

 //如果父类加载器抛出ClassNotFoundException

 //说明父类加载器无法完成加载请求

 }

 if(c==null){

 //在父类加载器无法加载的时候

 //再调用本身的findClass方法来进行类加载

 c=findClass(name);

 }

 }

 if(resolve){

 resolveClass(c);

 }

 return c;

 }

java.class.ClassLoader 类API

– getParent() 返回该类加载器的父类加载器。
– loadClass(String name) 加载名称为 name的类,返回的结果是 java.lang.Class类的实例。
– findClass(String name) 查找名称为 name的类,返回的结果是 java.lang.Class类的实例。
– findLoadedClass(String name) 查找名称为 name的已经被加载过的类,返回的结果是 java.lang.Class类的实例。
– defineClass(String name, byte[] b, int off, int len) 把字节数组 b中的内容转换成 Java 类,返回的结果是java.lang.Class类的实例。这个方法被声明为 final的。
– resolveClass(Class<?> c) 链接指定的 Java 类。

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值