JVM的类加载

一、基本类加载机制介绍

大体引用一下《深入理解Java虚拟机》一书中对类加载的定义:虚拟机将描述类的二进制字节流(即Class文件)加载到内存中,并对其进行验证、准备、解析、初始化,最终

生成可以直接被虚拟机使用的Java类型(即已经校验合格且有clinit执行完clinit方法的Class对象),这就是JVM的类加载机制。

一个好的定义,就应该是这样准确而简练的。下面先罗列一下类的生命周期:

加载  ->  验证  ->  准备 ->  解析  ->  初始化  ->  使用  -> 卸载

其中加载、验证、准备、初始化、卸载一定会严格按照上面的顺序进行。既然知道了类加载的大体流程,那么问题来了,什么时候会触发类的加载呢?

虚拟机规范中并未作出明确的要求,但是会要求有且仅有下面五种情况发生的时候,会触发类的初始化(言外之意就是初始化之前的动作必须在此之前完成),这五种情况分别为:

1、遇到new、getStatic、putStatic、invokeStatic字节码指令时;

2、通过java.lang.reflect包对类进行反射调用的时候;

3、初始化子类时,如果父类没初始化,则先初始化父类(接口除外,接口初始化规则是调用哪个接口初始化哪个,不看继承关系);

4、虚拟机启动时,会自动初始化带有main方法的类;

5、使用动态语言支持时,如果一个MethodHandle实例最后的解析结果是REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄且对应的类未初始化时(恕BZ学识浅薄,目前还没

理解这种情况对应实际代码中的哪种情形,只能先放放,好在这东西一般也遇不到 (><) )。

额外需要说明两点,这两点也经常在那种仿佛是考脑筋急转弯的编程题中遇到:一是在new一个引用类数组时不会触发这个类的初始化;二是对于常量的引用不会触发类初始化(static final)。

尤其是第二点要特别说明一下,如果A类中有一个常量叫VALUE,B类中引用了这个常量,那么在编译的时候,编译器会把这个VALUE直接放入B类的运行时常量池中供其后续使用,就是说在

编译之后,A类与B类已经没有任何交互了,所以也就不会触发初始化了。

二、类加载的各个过程简介

1、加载

类加载的第一个阶段叫加载,主要做的是三步:

1)、通过类的全限定名定位到对应的class文件;

2)、将二进制字节文件转化为方法区中的运行时数据;

3)、在内存中生成一个Class对象,作为方法区中访问运行时数据(即访问2)中数据)的入口。

此阶段自由度较高,我们可以自定义一个类加载器,加载class文件

2、验证

验证主要包括四部分:文件格式校验、源数据校验(包括语义校验)、字节码校验、字符引用校验

3、准备

在此阶段,虚拟机会为类变量(即static修饰的成员变量)分配内存并赋初始的默认值。例如一个类变量private static int a = 2,在此阶段中,会给赋初值,即a=0。实际的2这个值是在

初始化阶段赋予。而如果是static final类型的变量,在此阶段会直接赋终值。

4、解析

将要加载的那个类的运行时常量池内的符号引用替换为直接引用

5、初始化

初始化的过程就是执行类构造器<clinit>()方法的过程。此方法是在编译阶段由编译器收集类中的静态变量赋值动作、静态方法、静态块的语句合并而成的。

虚拟机会保证执行<clinit>()方法前会先执行父类的<clinit>方法,同样如果是接口则不用先执行父类的类构造器。

三、类加载器

Java中一共定义了三种类加载器:启动类加载器  <--   扩展类加载器   <--  应用程序类加载器,继承关系如箭头所示,而我们自定的类加载器,都是以应用程序类加载器为父类。

类加载器很重要,因为JVM中定义两个类是不是同一个类,只用两个判断条件:一个是类的全限定名是否相同,另一个是加载这个类的类加载器是否相同。如果类加载器不同,

即使类的全限定名一样,虚拟机一样会判定这是两个不同的类。

Java中的类加载器加载类,采用双亲委派模型,即类加载器C要加载类A时,会先让父加载器去加载,一直往上传递,除非父类加载器中搜不到这个类,才会让子类去加载,并且

每个类只能被加载一次。这样做的好处就是可以限定核心类不会被替换篡改。比如一种常见的面试题,如果你重新编写了一个路径为java.lang.String的类,能否调用到这个自定的类

中的方法?在这里很明显答案是不会,因为JVM中已经加载了那个系统的java.lang.String类,当用应用程序类加载器加载我们自定义的String类时,由于双亲委派模型的存在,加载

任务会被委派给父类,一直往上传递,最后到启动类加载器那里,然后发现已经加载过一次这个全路径名的类了,不能重复加载,所以自定义的String类就无法被加载调用了。(有没

有发现这个剧情跟真假美猴王很相似,一直传递到佛祖那,才辨别出了真假 。。。)

类加载就暂时到这里,书中还提到一些双亲委派模型被破坏的场景,由于了解的不够透彻,就先不乱发言了。学习之路,还是要坚持死磕啊,加油!

原文地址:https://www.cnblogs.com/zzq6032010/p/10127717.html

时间: 2024-11-05 18:32:13

JVM的类加载的相关文章

jvm之类加载器-《疯狂java讲义》

1. 类加载器简介 类加载器负责加载所有的类,系统为所有被载入内存中的类生成一个java.lang.Class实例.一旦一个类被载入JVM中,同一个类就不会被再次载入了.现在的问题是怎么样才算“同一个类”?正如一个对象有一个唯一的标识一样,一个载入JVM的类也有一个唯一的标识. 同理,载入JVM的类也有一个唯一的标识,在java中,一个类用其全限定类名(包括包名和类名)作为标识.但在JVM中,一个类用其全限定类名和其类加载器作为其唯一的标识.因此,如果在pg包中,有一个名为Person的类,被类

深入JVM之类加载机制

JVM系列 1.深入JVM之源码编译机制 2.深入JVM之类加载机制 3.深入JVM之类执行机制 4.JVM内存诊断工具 总结下类加载机制相关的知识图谱. 转载请注明出处:http://blog.csdn.net/supera_li/article/details/45747329 类加载机制,包括三大步骤. 1.装载load .目的是生成类加载实例id 2.链接 ,检测字节码格式以及内部调用匹配情况.静态变量初始化 3.初始化.主要是类加载器加载类的顺序. 对于自定义类加载,开发者完成自己的实

JVM自定义类加载器加载指定classPath下的所有class及jar

一.JVM中的类加载器类型 从Java虚拟机的角度讲,只有两种不同的类加载器:启动类加载器和其他类加载器. 1.启动类加载器(Boostrap ClassLoader):这个是由c++实现的,主要负责JAVA_HOME/lib目录下的核心 api 或 -Xbootclasspath 选项指定的jar包装入工作. 2.其他类加载器:由java实现,可以在方法区找到其Class对象.这里又细分为几个加载器 a).扩展类加载器(Extension ClassLoader):负责用于加载JAVA_HOM

JVM之类加载机制

JVM之类加载机制 JVM类加载机制分为五个部分:加载,验证,准备,解析,初始化,下面我们就分别来看一下这五个过程. 类加载五部分 加载 加载是类加载过程中的一个阶段,这个阶段会在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的入口.注意这里不一定非得要从一个Class文件获取,这里既可以从ZIP包中读取(比如从jar包和war包中读取),也可以在运行时计算生成(动态代理),也可以由其它文件生成(比如将JSP文件转换成对应的Class类). 验证 这一阶

深入理解JVM之类加载

---title: [学习]深入理解JVM之类加载.mddate: 2019-10-20 22:20:06tags: JVM 类加载--- Java类的加载,连接,初始化都是在程序运行期间执行的 ## Java 虚拟机与程序的生命周期 1. 执行 System.exit()方法2. 程序正常结束3. 遇到异常或错误终止4. 由于操作系统或程序虚拟机进程错误 以上的情况都可以结束生命周期 ## Java 类加载的方式 1. 本地系统直接加载2. 通过网络下载.class 文件3. 通过zip,ja

漫谈JVM之类加载机制(篇一)

前言 最近在看一本书,发现代码里用到了Thread.currentThread().getContextClassLoader(),为什么类加载器还与线程有关系呢,为什么不直接使用ClassLoader.getSystemClassLoader()呢?带着这些疑问又把JVM类加载机制从头到尾学习了一遍. 篇一 类加载时机 我们编写的代码存储在java文件中,java源代码通过编译生成Java虚拟机可识别的字节码,存储在Class文件中.运行java程序时需要将Class文件中的信息加载到Java

JVM的类加载机制

虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 类加载的过程: 包括加载.链接(含验证.准备.解析).初始化 如下图所示: 1.加载: 类加载指的是将类的class文件读入内存,并为之创建一个java.lang.Class对象,作为方法区这个类的数据访问的入口. 也就是说,当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象.具体包括以下三个部分: (1)通过类的全

jvm自定义类加载器

除了自定义的类加载之外,jvm存在三种类加载器,并以一种父委托的加载机制进行加载. --启动类加载器,又称根加载器,是一个native的方法,使用c++实现.在java中我们用null标识,用于加载jdk自带的类. --扩展类加载器,用于加载jdk扩展类 --系统类加载器,用于加载classpath目录下的类 上面提到的三种类加载器,是存在父子关系,即系统类加载器会委托extension加载器,如果extension加载器不能加载该类的话,再由系统类加载器进行加载.注意这里所说的父子关系不是指继

JVM之类加载器

一.首先,小小测试,看是否已经掌握了JVM类加载的过程 1.1.测试一: class Singleton { private static Singleton sin = new Singleton(); public static int counter1; public static int counter2 = 0; private Singleton() { counter1++; counter2++; } public static Singleton getInstance() {