深入理解Java虚拟机类加载机制
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虚拟机类加载机制的更多相关文章
- 深入理解Java虚拟机---类加载机制(简略版)
类加载机制 谈起类加载机制,在这里说个题外话,当初本人在学了两三个月的Java后,只了解了一些皮毛知识,就屁颠屁颠得去附近学校的招聘会去蹭蹭面试经验,和HR聊了一会后开始了技术面试,前抛出了两个简单的 ...
- 面试官,不要再问我“Java虚拟机类加载机制”了
关于Java虚拟机类加载机制往往有两方面的面试题:根据程序判断输出结果和讲讲虚拟机类加载机制的流程.其实这两类题本质上都是考察面试者对Java虚拟机类加载机制的了解. 面试题试水 现在有这样一道判断程 ...
- [转]Java虚拟机类加载机制
原文地址:http://blog.csdn.net/u013256816/article/details/50829596 看到这个题目,很多人会觉得我写我的java代码,至于类,JVM爱怎么加载就怎 ...
- 面试官,不要再问我“Java虚拟机类加载机制”了(转载)
关于Java虚拟机类加载机制往往有两方面的 面试题:根据程序判断输出结果和讲讲虚拟机类加载机制的流程.其实这两类题本质上都是考察面试者对Java虚拟机类加载机制的了解. 面试题试水 现在有这样一道判断 ...
- 【转载】Java虚拟机类加载机制与案例分析
出处:https://blog.csdn.net/u013256816/article/details/50829596 https://blog.csdn.net/u013256816/articl ...
- Java虚拟机类加载机制——案例分析
转载: Java虚拟机类加载机制--案例分析 在<Java虚拟机类加载机制>一文中详细阐述了类加载的过程,并举了几个例子进行了简要分析,在文章的最后留了一个悬念给各位,这里来揭开这个悬 ...
- java虚拟机类加载机制和双亲委派模型
java虚拟机类加载机制:虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的java类型. 类的生命周期是从类被加载到虚拟机内存中,到卸 ...
- Java 虚拟机类加载机制
看到这个题目,很多人会觉得我写我的java代码,至于类,JVM爱怎么加载就怎么加载,博主有很长一段时间也是这么认为的.随着编程经验的日积月累,越来越感觉到了解虚拟机相关要领的重要性.闲话不多说,老规矩 ...
- 十年Java程序员-带你走进Java虚拟机-类加载机制
类的生命周期 1.加载 将.class文件从磁盘读到内存 2.连接 2.1 验证 验证字节码文件的正确性 2.2 准备 给类的静态变量分配内存,并赋予默认值 2.3 解析 类装载器装入类所引用的其它所 ...
随机推荐
- dubbo filter实现接口认证springboot idea
最近公司有业务需求,要对Dubbo接口调用者进行身份验证,验证通过才能调用,网上一些资料不够全面,遂整理了一下. 在provider方定义一个filter,需要实现com.alibaba.dubbo. ...
- python调用百度语音(语音识别-斗地主语音记牌器)
一.概述 本篇简要介绍百度语音语音识别的基本使用(其实是斗地主时想弄个记牌器又没money,抓包什么的又不会,只好搞语音识别的了) 二.创建应用 打开百度语音官网,产品与使用->语音识别-> ...
- 史上最全的FTP网址
无帐号密码的为匿名登录 ftp://202.114.1.121 ftp://202.114.10.199 ftp://warez:cn.ftp@202.114.12.174 ftp://Music2: ...
- Spring Boot 2.0(二):Spring Boot 2.0尝鲜-动态 Banner
Spring Boot 2.0 提供了很多新特性,其中就有一个小彩蛋:动态 Banner,今天我们就先拿这个来尝尝鲜. 配置依赖 使用 Spring Boot 2.0 首先需要将项目依赖包替换为刚刚发 ...
- AGC010 - B: Boxes
原题链接 题意简述 给出一个由个数构成的环,每次可以选择一个位置并从这个数起顺时针依次对每个数-1,-2,-3,-,-n.问能否将所有数全变为0. 分析 考虑一次操作对环带来了什么影响. (在后加一个 ...
- 【Learning】 动态树分治
简介 动态树分治整体上由点分治发展而来. 点分治是统计树上路径,而动态树分治用来统计与点有关的树上路径,比如多次询问某一些点到询问点的距离和. 前置知识就是点分治. 做法 众所周知,点分树(点分治中重 ...
- spring boot actuator 简单使用
spring boot admin + spring boot actuator + erueka 微服务监控 简单的spring boot actuator 使用 POM <dependenc ...
- Effective Java 第三版——35. 使用实例属性替代序数
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
- Visual Studio Code搭建python开发环境
开发Python的环境有很多,原来已经在vs2013上面搭建好python的开发环境了,但是vs2013每次启动都占太多内存(太强大了吧),这下出了vs code,既轻量又酷炫,正好拿来试一试开发py ...
- 实战DeviceIoControl 之二:获取软盘/硬盘/光盘的参数
Q 在MSDN的那个demo中,将设备名换成"A:"取A盘参数,先用资源管理器读一下盘,再运行这个程序可以成功,但换一张盘后就失败:换成"CDROM0"取CDR ...