1.类加载时机

对于类加载的第一个阶段—--加载,虚拟机没有强制的约束,但是对于初始化阶段,虚拟机强制规定有且只有以下的5中情况必须开始初始化,当然,加载、验证、准备阶段在初始化前就已经开始。

  ①使用new关键字实例化对象的时候、读取或设置一个类的静态字段(被final修饰静态字段除外)的时候,以及调用一个类的静态方法的时候。

  ②对类进行反射调用的时候

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

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

  ⑤当使用JDK 1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。

  上面的5种场景中的行为称为对一个类的主动引用。除此之外的其他引用,虚拟机都不会触发类的初始化,称为被动引用。

  被动引用的例子:

/*
*通过子类引用父类的静态字段,不会导致子类初始化
**/
public class SuperClass {
static {
System.out.println("SuperClass init!");
}
public static int value = 123;
}
public class SubClass extends SuperClass{
static{
System.out.println("SubClass init!");
}
}
/**
*非主动使用类字段演示
**/
public class NotInitialization{
public static void main(String[]args){
System.out.println(SubClass.value);
}
}

结果:只会输出SuperClass init!

/**
*被动使用类字段演示二:
*通过数组定义来引用类,不会触发此类的初始化
**/
public class NotInitialization{
public static void main(String[]args){
SuperClass[]sca=new SuperClass[10];
}
}

结果:不会输出SuperClass init!

/**
*非主动使用类字段演示
**/
public class NotInitialization{
public static void main(String[]args){
System.out.println(ConstClass.HELLOWORLD);
}
}

结果:没有输出“ConstClass init!”

接口加载过程:只与类加载过程中的第三条不同,当一个类在初始化时,要求其父类全部都已经初始化过了,但是一个接口在初始化时,并不要求其父接口全部都完成了初始化,只有在真正使用到父接口的时候(如引用接口中定义的常量)才会初始化。

2. 类加载过程

2.1加载:

    加载阶段虚拟机要完成三件事:

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

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

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

2.2 连接:

    2.2.1验证:

      验证阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。虚拟机主要做以下工作:

      ①文件格式验证:例如:以魔数0xCAFEBABE开头

      ②元数据验证:例如:是否有父类

      ③字节码验证:对类的方法体验证

      ④符号引用验证:例如:通过全限定名是否能找到对应的类

    2.2.2 准备:

      准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。如:

      public static int value=123;

      准备阶段只会把类变量的初始值(也就是0)赋值给value,而真正到初始化阶段才把123赋值给value。

      注意:如果是final修饰的字符串,虚拟机会在准备阶段就对变量做初始化。如下:

      public static final int value=123;

      编译时Javac将会为value生成ConstantValue属性,在准备阶段虚拟机就会根据ConstantValue的设置将value赋值为123。

    2.2.3 解析:

      解析阶段解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行解析。

  2.3初始化:

      在此之前的所有动作都是虚拟机来主导的,到了初始化阶段才真正执行类中定义的Java代码。也就是说由程序员写的用来初始化类变量或其他资源的代码在初始化阶段才得以执行。

      事实上,初始化过程就是是执行类构造器<clinit>()方法的过程,<clinit>()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{}块)中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序所决定的。

      静态语句块中只能访问到定义在静态语句块之前的变量,定义在它之后的变量,在前面的静态语句块可以赋值,但是不能访问。如下代码:

//非法向前引用变量:
public class Test{
static{
i=0;//给变量赋值可以正常编译通过
System.out.print(i);//这句编译器会提示"非法向前引用"
}
static int i=1;
}

3.类加载器

  3.1类与类加载器:

 通过一个类的全限定名(如:反射)来获取描述此类的二进制字节流类加载器,这个动作的代码模块称为类加载器。

   判断两个类是否相等:

   ①类名是否相同,包括包名;

   ②是否由同一个类加载器加载,只有在两个类由同一个类加载器加载的前提下才有意义,否则即使这两个类都来源于同一个class文件,而加载这个class文件的类加载器不同,那么这两个类也是不相等的。

   虚拟机提供了3种类加载器,引导(Bootstrap)类加载器、扩展(Extension)类加载器、系统(System)类加载器(也称应用类加载器)。

   启动类加载器:<JAVA_HOME>/lib路径下的核心类库或-Xbootclasspath参数指定的路径下的jar包,出于安全考虑,只加载包名为java、javax、sun等开头的类。

   扩展类加载器:加载<JAVA_HOME>/lib/ext目录下或者由系统变量-Djava.ext.dir指定位路径中的类库

   应用程序类加载器:也称它为系统类加载器。加载系统类路径java -classpath或-D java.class.path 指定路径下的类库,也就是我们经常用到的classpath路径,开发者可以直接使用系统类加载器。

3.2 双亲委派模式:

当类加载器加载一个类的时候,它先委托其父类加载器加载这个类,同样的,父类加载器也委托其父类加载器加载这个类,一直到顶层的启动类加载器,启动类加载器默认是没有父类的,当启动类加载器无法加载此类时,就会委派给其子类加载器,同样的,一直委派下去,直到有子类加载器能成功加载此类,否则就报错。这就是双亲委派模式。

   使用双亲委派模式的优势:

 ①避免类的重复加载:当父类已经加载了这个类,子类就没有必要再加载一次。

 ②防止核心api库被篡改:当从网络中传过来一个java.lang.Object类时,类加载器通过双亲委派模式委派到启动类加载器,启动类加载器发现这个类已经加载了,就不会再去加载传过来的这个类,而是直接返回已经加载的类。

 破坏双亲委派模式:

    ①使用线程上下文类加载器,当基础类要调用回用户的代码,这时由于双亲委派模式的存在,启动类加载器并不能委派给子类加载器去加载类,此时就要用到线程上下文类加载器来解决。常见有JNDI、JDBC、JCE、JAXB和JBI等。

    以jdbc为例:<JAVA_HOME>/lib下已经封装好了jdbc的接口,由各个不同的数据库厂商按照接口的规范来编写这些接口的实现类,但是这些实现类通常都是放在classpath路径下,启动类加载器需要调用这些接口的实现类,但它并不能加载classpath路径下的类,classpath路径下的类只能由系统类加载器加载,又由于双亲委派模式的存在,只能由子类加载器委托父类加载器加载类,不能由父类加载器委派给子类加载器加载类。于是便出了一个折中的办法,当启动类加载器需要调用这些接口的实现类时,它委托线程上下文类加载器去加载这些实现类。默认情况下,线程上下文类加载器就是系统类加载器。

    ②代码热替换(HotSwap)、模块热部署(Hot Deployment):

    意思就是希望应用程序能像我们的计算机外设那样,接上鼠标、U盘,不用重启机器就能立即使用,鼠标有问题或要升级就换个鼠标,不用停机也不用重启。具体应用例子有Tomcat下的项目的自动发布,jsp修改后无需重启服务器等。

4.Tomcat分析

  Common类加载器:加载tomcat路径下的lib目录下class文件

  Webapp类加载器:加载tomcat路径下的/WebApp/WEB-INF/*下class文件

  Jsp类加载器:它的加载范围仅仅是这个JSP文件所编译出来的那一个Class,它出现的目的就是为了被丢弃:当服务器检测到JSP文件被修改时,会替换掉目前的JasperLoader的实例,并通过再建立一个新的Jsp类加载器来实现JSP文件的HotSwap功能。

  WebApp类加载器和Jsp类加载器通常会存在多个实例,每一个Web应用程序对应一个WebApp类加载器,每一个JSP文件对应一个Jsp类加载器。

深入理解Java虚拟机类加载机制的更多相关文章

  1. 深入理解Java虚拟机---类加载机制(简略版)

    类加载机制 谈起类加载机制,在这里说个题外话,当初本人在学了两三个月的Java后,只了解了一些皮毛知识,就屁颠屁颠得去附近学校的招聘会去蹭蹭面试经验,和HR聊了一会后开始了技术面试,前抛出了两个简单的 ...

  2. 面试官,不要再问我“Java虚拟机类加载机制”了

    关于Java虚拟机类加载机制往往有两方面的面试题:根据程序判断输出结果和讲讲虚拟机类加载机制的流程.其实这两类题本质上都是考察面试者对Java虚拟机类加载机制的了解. 面试题试水 现在有这样一道判断程 ...

  3. [转]Java虚拟机类加载机制

    原文地址:http://blog.csdn.net/u013256816/article/details/50829596 看到这个题目,很多人会觉得我写我的java代码,至于类,JVM爱怎么加载就怎 ...

  4. 面试官,不要再问我“Java虚拟机类加载机制”了(转载)

    关于Java虚拟机类加载机制往往有两方面的 面试题:根据程序判断输出结果和讲讲虚拟机类加载机制的流程.其实这两类题本质上都是考察面试者对Java虚拟机类加载机制的了解. 面试题试水 现在有这样一道判断 ...

  5. 【转载】Java虚拟机类加载机制与案例分析

    出处:https://blog.csdn.net/u013256816/article/details/50829596 https://blog.csdn.net/u013256816/articl ...

  6. Java虚拟机类加载机制——案例分析

    转载: Java虚拟机类加载机制--案例分析   在<Java虚拟机类加载机制>一文中详细阐述了类加载的过程,并举了几个例子进行了简要分析,在文章的最后留了一个悬念给各位,这里来揭开这个悬 ...

  7. java虚拟机类加载机制和双亲委派模型

    java虚拟机类加载机制:虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的java类型. 类的生命周期是从类被加载到虚拟机内存中,到卸 ...

  8. Java 虚拟机类加载机制

    看到这个题目,很多人会觉得我写我的java代码,至于类,JVM爱怎么加载就怎么加载,博主有很长一段时间也是这么认为的.随着编程经验的日积月累,越来越感觉到了解虚拟机相关要领的重要性.闲话不多说,老规矩 ...

  9. 十年Java程序员-带你走进Java虚拟机-类加载机制

    类的生命周期 1.加载 将.class文件从磁盘读到内存 2.连接 2.1 验证 验证字节码文件的正确性 2.2 准备 给类的静态变量分配内存,并赋予默认值 2.3 解析 类装载器装入类所引用的其它所 ...

随机推荐

  1. Yii2 场景

    下面给大家介绍一下 yii2.0 场景的使用. 现在在 post表里面有 title image content 三个的字段,当我创建一个 post 的时候,我想三个字段全部是必填项,但是你修改的时候 ...

  2. Flask從入門到入土(五)——Flask与数据库

    import sqlite3 def init_db(): sql = 'create table user(id INT,name TEXT)' conn = sqlite3.connect(&qu ...

  3. python高阶函数式编程

    from functools import reduce def str2int(s): def fn(x, y): return x * 10 + y def char2num(s): return ...

  4. asp.net动态网站repeater控件使用及分页操作介绍

    asp.net动态网站repeater控件使用及分页操作介绍 1.简单介绍 Repeater 控件是一个容器控件,可用于从网页的任何可用数据中创建自定义列表.Repeater 控件没有自己内置的呈现功 ...

  5. jquery 上传图片转为base64,ajax提交到后台

    支持多张图片上传.图片上传数量修改.可以删除 <!DOCTYPE html> <html> <head> <meta charset="utf-8& ...

  6. 【前端】Vue2全家桶案例《看漫画》之二、完成首页基本样式

    转载请注明出处:http://www.cnblogs.com/shamoyuu/p/vue_vux_app_2.html 项目github地址:https://github.com/shamoyuu/ ...

  7. Java利用递归算法统计1-6的数组排列组合数

    Java利用递归算法统计1-6的数组排列组合数 1.设计源码 /** * @Title:ArrayCombination.java * @Package:com.you.data * @Descrip ...

  8. 【mongodb系统学习之十一】mongodb删除数据

    十一.mongodb删除数据: 1).删除全部文档:remove,语法db.collectionName.remove({}):小括号里边必须要有条件,否则不成功:如果只是一个空的{},则会删除集合内 ...

  9. MPEG2_TS流基本概念和数据结构

    时对应network_PID,program_number等于其它值时对应program_map_PID. (3)PMT   PMT数据结构如下: TS_program_map_section(){ ...

  10. java.util.zip.ZipException:ZIP file must have at least one entry

    1.错误描述 java.util.zip.ZipException:ZIP file must have at least one entry 2.错误原因 由于在导出文件时,要将导出的文件压缩到压缩 ...