学习java虚拟机 - 类加载机制

学习java虚拟机 - 类加载机制

 一、是什么

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

  在Java语言里面,类型的加载、链接、初始化过程都是在程序运行期间完成的,Java里天生可以动态扩展的语言特性就是依赖运行期间动态加载和动态连接这个特点实现的。例如,如果编写一个面向接口的应用程序,可以等到运行时在制定实际的实现类;用户可以通过Java预定义的和自定义类加载器,让一个本地的应用程序可以在运行时从网络或其他地方加载一个二进制流作为程序代码的一部分。

二、类加载过程

  类从被加载到虚拟机内存中开始,到卸载出内存位置,它的整个生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7个阶段。其中,验证、准备、解析三个阶段统称为链接(Link)。

  1) 加载(Loading):

  1.1))通过一个类的全限定名来获取定义此类的二进制字节流。(获取的方式可以自定义实现,可以从.class文件中获取,可以从网络中获取,可以从zip包中读取,可以运行时计算生成)

    1.2)将字节流所代表的静态存储结构转成方法区的运行时数据结构。

    1.3)在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的入口。(这与反射的应用相关)

  2)验证(Verification)

    目的是为了确保Class字节流包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。    

    2.1) 文件格式验证

    验证字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理。

    2.2)元数据验证

    对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言规范的要求。

    2.3)字节码验证

    通过数据流和控制流分析,确定程序语义是否合法、符合逻辑。

    2.4)符号引用验证

  在解析阶段发生,即将符号引用转化为直接引用的时候发生。目的是确保解析动作能正常执行。

  对于虚拟机的类加载机制来说,验证阶段是一个非常重要、但非必要的阶段。如果所运行的全部代码已经被反复使用和验证过,在实施阶段就可以考虑使用-Xverify:none参数来关闭大部分的类验证措施,以缩短虚拟机类加载时间。

  3)准备(Preparing)

  正式为类变量(非实例变量)分配内存并设置类变量初始值(零值)。类变量所使用的内存都在方法区中分配,实例变量会在对象实例化时随着对象一起分配在Java堆中。

  public static int value=123;

  value会被设置为初始值0, 而把value赋值为123的putstatic指令是程序被编译后,存放于类构造器<clinit>()方法中,所以把value赋值为123的动作会在初始化阶段执行。

  4)解析(Resolution)

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

  符号引用(Symbolic Refrences):符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。

  直接引用(Direct References): 直接引用可以是直接执行目标的指针、相对偏移量或是一个能间接定位到目标的句柄。

    

  除了invokedynamic指令以外,虚拟机实现可以对第一次解析的结果进行缓存(在运行时常量池中记录直接引用,并把常量标识为已解析状态)从而避免解析动作重复进行。

  invokedynamic指令的目的适用于动态语言支持,它所对应的引用称为“动态调用点限定符”(Dynamic Class Site Specifier), 等到程序执行到这条指令的时候解析动作在能进行。

  5)初始化(Initialization)

  类初始化阶段是类加载过程的最后一步,在准备阶段,变量已经付过一次系统要求的初始值,而在初始化阶段,则根据程序员通过程序制定的主观计划去初始化类变量和其他执行。

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

三、双亲委派模型、类加载器

  虚拟机设计团队把类加载阶段中的”通过一个类的全限定名来获取描述此类的二进制字节流“这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称为”类加载器“。

  对于任意一个类,需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性。比较两个类是否”相等“,只有在这两个类是由同一个类加载器的前提下才有意义,否则,及时这两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载他们的类加载器不同,那这两个类就必定不同。

  3.1) 三种加载器

  1) 启动类加载器(Bootstrap ClassLoader) , 这个类加载器使用C++语言实现,是虚拟机自身的一部分。

  负责将存放在<JAVA_HOME>\lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且被虚拟机识别的(仅按照文件名识别)类库加载到虚拟机内存中。

  2) 扩展类加载器(Extension ClassLoader):

    负责加载<JAVA_HOME>\lib\ext目录中的,或者被java.ext.dirs系统变量所制定的路径中的所有类库。

  3)   应用程序类加载器(Application ClassLoader)

  程序中默认的类加载器,一般称它为系统类加载器,因为它是ClassLoader#getSystemClassLoader()方法的返回值。它负责加载用户类路径(Classpath)上所指定的类库。

  3.2) 双亲委派模型的工作过程:

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

  破坏双亲委派模型:

  a) jdk1.0时代, 并未实现双亲委派模型, 为引入双亲委派模型, 而不得不"破坏"之前的设计.

  b) 线程上下文类加载器。

  “基础”代码,总是作为被用户代码调用的API,如果基础类想要调回用户的代码, 就需要线程上下文类加载器了.

  如JNDI, JDNI的目的是对资源进行集中管理和查找,它需要调用由独立仓上实现并部署在应用程序的ClassPath下的JNDI提供者的代码。然而父类加载器并不"认识"classpath路径下的类, 所以需要使用线程上下文类加载器加载该类, 也就是父类加载器请求子类加载器完成类加载的动作.线程上下文类加载器通过Thread的setClassLoader()来设置, 线程创建时, 没有设置, 则从父类加载器继承一个, 如果应用程序全局范围内都没有设置, 则默认是应用类加载器.

  c) 热部署

  OSGI热部署. 每个程序模块(OSGI 成为bundle)都有一个自己的类加载器. 当需要更换一个bundle时, 就联通bundle中的类加载器也一起换掉, 以达到热部署的目的.

学习资料:

  <深入理解java虚拟机>

原文地址:https://www.cnblogs.com/timfruit/p/10501874.html

时间: 2024-12-26 20:52:26

学习java虚拟机 - 类加载机制的相关文章

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

原文出处: 朱小厮 在<Java虚拟机类加载机制>一文中详细阐述了类加载的过程,并举了几个例子进行了简要分析,在文章的最后留了一个悬念给各位,这里来揭开这个悬念.建议先看完<Java虚拟机类加载机制>这篇再来看这个,印象会比较深刻,如若不然,也没什么关系~~下面是程序代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package jvm.cla

[转]Java虚拟机类加载机制浅谈

Java语言是一种编译后再经过解释器执行的过程, 解释器主要就是如何处理解释Class文件的二进制字节流.JVM主要包含三大核心部分:运行时数据区,类加载器和执行引擎. 虚拟机将描述类的数据从Class文件加载到内存,并对数据进行校验.准备.解析和初始化,最终就会形成可以被虚拟机使用的Java类型,这就是一个虚拟机的类加载机制.Java中的类是动态加载的,只有在运行期间使用到该类的时候,才会将该类加载到内存中,Java依赖于运行期动态加载和动态链接来实现类的动态使用. 一个类的整个生命周期如下:

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

java虚拟机类加载机制:虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的java类型. 类的生命周期是从类被加载到虚拟机内存中,到卸载出内存为止: 类的生命周期: 加载 loading . 验证 verification. 准备 preparation. 解析 resolution. 初始化 initialization. 使用 using. 卸载 unloading 类加载器的层次结构: 双亲委派模型过程: 某个特定的类加载器

Java虚拟机类加载机制

原文出处: 朱小厮 看到这个题目,很多人会觉得我写我的java代码,至于类,JVM爱怎么加载就怎么加载,博主有很长一段时间也是这么认为的.随着编程经验的日积月累,越来越感觉到了解虚拟机相关要领的重要性.闲话不多说,老规矩,先来一段代码吊吊胃口. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public cla

深入理解Java虚拟机- 学习笔记 - 虚拟机类加载机制

虚拟机把描述类的数据从Class文件加载道内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制.在Java里,类型的加载.连接和初始化过程都是在程序运行期间完成的,这种策略虽然会令类加载时稍微增加一些性能开销,但是会为Java应用程序提供高度的灵活性. 一.类加载的时机 类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载(Loading).验证(Verification).准备(Preparation).解析(Re

Java虚拟机 - 类加载机制

[深入Java虚拟机]之四:类加载机制 类加载过程     类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载.验证.准备.解析.初始化.使用和卸载七个阶段.它们开始的顺序如下图所示: 其中类加载的过程包括了加载.验证.准备.解析.初始化五个阶段.在这五个阶段中,加载.验证.准备和初始化这四个阶段发生的顺序是确定的,而解析阶段则不一定,它在某些情况下可以在初始化阶段之后开始,这是为了支持Java语言的运行时绑定(也成为动态绑定或晚期绑定).另外注意这里的几个阶段是按顺序开

JAVA 虚拟机类加载机制和字节码执行引擎

引言 我们知道java代码编译后生成的是字节码,那虚拟机是如何加载这些class字节码文件的呢?加载之后又是如何进行方法调用的呢? 一 类文件结构 无关性基石 java有一个口号叫做一次编写,到处运行.实现这个口号的就是可以运行在不同平台上的虚拟机和与平台无关的字节码.这里要注意的是,虚拟机也是中立的,只要是符合规范的字节码,都可以被虚拟机接受,例如Groovy,JRuby等语言,都会生成符合规范的字节码,然后被虚拟机所运行,虚拟机不关心字节码由哪种语言生成. 类文件结构 class类文件是一组

深入了解java虚拟机---类加载机制----加载

加载是类加载的一个阶段.不要弄混淆了 这一阶段主要做了下面三件事.1.通过一个类的全限定名获取此类的二进制流 2.将这个二进制流代表的静态存储结构转化为方法区的运行时数据结构 3.在内存中生成java.lang.Class对象,作为访问入口 .通过一个类的全限定名获取此类的二进制流:这一步说的不明确,要怎样获取呢.java虚拟机没有指明.所以就产生了很多种获取方法 1.从zip,jar,war包获取 2.从网络中获取 3.运行时生成,如动态代理 4.有其他文件生成 如jsp 5.从数据库中获取,

Java虚拟机——类加载机制

转自:http://blog.csdn.net/ns_code/article/details/17881581 类加载过程 类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载.验证.准备.解析.初始化.使用和卸载七个阶段.它们开始的顺序如下图所示: 其中类加载的过程包括了加载.验证.准备.解析.初始化五个阶段.在这五个阶段中,加载.验证.准备和初始化这四个阶段发生的顺序是确定的,而解析阶段则不一定,它在某些情况下可以在初始化阶段之后开始,这是为了支持Java语言的运行时