我们知道我们编写的java代码,会经过编译器编译成字节码文件(class文件),再把字节码文件装载到JVM中,映射到各个内存区域中,我们的程序就可以在内存中运行了。那么字节码文件是怎样装载到JVM中的呢?中间经过了哪些步骤?常说的双亲委派模式又是怎么回事?本文主要搞清楚这些问题。

一、类装载流程

1、加载

加载是类装载的第一步,首先通过class文件的路径读取到二进制流,并解析二进制流将里面的元数据(类型、常量等)载入到方法区,在java堆中生成对应的java.lang.Class对象。

2、连接

连接过程又分为3步,验证、准备、解析

1)、验证

验证的主要目的就是判断class文件的合法性,比如class文件一定是以0xCAFEBABE开头的,另外对版本号也会做验证,例如如果使用java1.8编译后的class文件要再java1.6虚拟机上运行,因为版本问题就会验证不通过。除此之外还会对元数据、字节码进行验证,具体的验证过程就复杂的多了,可以专门查看相关资料去了解。

2)、准备

准备过程就是分配内存,给类的一些字段设置初始值,例如:

public static int v=1;

这段代码在准备阶段v的值就会被初始化为0,只有到后面类初始化阶段时才会被设置为1。

但是对于static final(常量),在准备阶段就会被设置成指定的值,例如:

public static final  int v=1;

这段代码在准备阶段v的值就是1。

3)、解析

解析过程就是将符号引用替换为直接引用,例如某个类继承java.lang.object,原来的符号引用记录的是“java.lang.object”这个符号,凭借这个符号并不能找到java.lang.object这个对象在哪里?而直接引用就是要找到java.lang.object所在的内存地址,建立直接引用关系,这样就方便查询到具体对象。

3、初始化

初始化过程,主要包括执行类构造方法、static变量赋值语句,staic{}语句块,需要注意的是如果一个子类进行初始化,那么它会事先初始化其父类,保证父类在子类之前被初始化。所以其实在java中初始化一个类,那么必然是先初始化java.lang.Object,因为所有的java类都继承自java.lang.Object。

说完了类加载过程,我们来介绍一下这个过程当中的主角:类加载器。

二、类加载器

类加载器ClassLoader,它是一个抽象类,ClassLoader的具体实例负责把java字节码读取到JVM当中,ClassLoader还可以定制以满足不同字节码流的加载方式,比如从网络加载、从文件加载。ClassLoader的负责整个类装载流程中的“加载”阶段。

1、ClassLoader的重要方法:

载入并返回一个类

public Class<?> loadClass(String name) throws ClassNotFoundException

定义一个类,该方法不公开被调用

protected final Class<?> defineClass(byte[] b, int off, int len)

查找类,loadClass的回调方法

protected Class<?> findClass(String name) throws ClassNotFoundException

查找已经加载的类

protected final Class<?> findLoadedClass(String name)

2、系统中的ClassLoader

BootStrap Classloader (启动ClassLoader)

Extension ClassLoader (扩展ClassLoader)

App ClassLoader(应用 ClassLoader)

Custom ClassLoader(自定义ClassLoader)

每个ClassLoader都有另外一个ClassLoader作为父ClassLoader,BootStrap Classloader除外,它没有父Classloader。

3、ClassLoader加载机制如下:

自下向上检查类是否被加载,一般情况下,首先从App ClassLoader中调用findLoadedClass方法查看是否已经加载,

如果没有加载,则会交给父类,Extension ClassLoader去查看是否加载,

还没加载,则再调用其父类,BootstrapClassLoader查看是否已经加载,

如果仍然没有,自顶向下尝试加载类,那么从 Bootstrap ClassLoader到 App ClassLoader依次尝试加载。

值得注意的是即使两个类来源于相同的class文件,如果使用不同的类加载器加载,加载后的对象是完全不同的,这个不同反应在对象的 equals()、isAssignableFrom()、isInstance()等方法的返回结果,也包括了使用 instanceof 关键字对对象所属关系的判定结果。

从代码上可以看出,首先查看这个类是否被加载,如果没有则调用父类的loadClass方法,直到BootstrapClassLoader(没有父类),我们把这个过程叫做双亲模式。

三、双亲模式的问题

顶层ClassLoader,无法加载底层ClassLoader的类

Java框架(rt.jar)如何加载应用的类?

比如:javax.xml.parsers包中定义了xml解析的类接口
Service Provider Interface SPI 位于rt.jar 
即接口在启动ClassLoader中。
而SPI的实现类,在AppLoader。

这样就无法用BootstrapClassLoader去加载SPI的实现类。

解决:

JDK中提供了一个方法:Thread. setContextClassLoader()

用以解决顶层ClassLoader无法访问底层ClassLoader的类的问题;
基本思想是,在顶层ClassLoader中,传入底层ClassLoader的实例。

四、双亲模式的破坏

双亲模式是默认的模式,但不是必须这么做;
Tomcat的WebappClassLoader 就会先加载自己的Class,找不到再委托parent;
OSGi的ClassLoader形成网状结构,根据需要自由加载Class。

五、小结

本文介绍了类加载的流程,以及ClassLoader工作机制,最后分析双亲模式的缺陷,以及如何弥补该缺陷,介绍了tomcat、OSGI如何自定义类加载流程。

本文转自:https://www.cnblogs.com/leefreeman/p/7429112.html

深入理解JVM一类加载器原理的更多相关文章

  1. 【深入理解JVM】类加载器与双亲委派模型 (转)

    出处: [深入理解JVM]类加载器与双亲委派模型 加载类的开放性 类加载器(ClassLoader)是Java语言的一项创新,也是Java流行的一个重要原因.在类加载的第一阶段“加载”过程中,需要通过 ...

  2. 【深入理解JVM】类加载器与双亲委派模型

    原文链接:http://blog.csdn.net/u011080472/article/details/51332866,http://www.cnblogs.com/lanxuezaipiao/p ...

  3. 深入理解JVM,类加载器

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

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

    一.JVM中的类加载器类型 从Java虚拟机的角度讲,只有两种不同的类加载器:启动类加载器和其他类加载器. 1.启动类加载器(Boostrap ClassLoader):这个是由c++实现的,主要负责 ...

  5. 深入理解:java类加载器

    概念理解:Java类加载器总结 1.深入理解Java类加载器(1):Java类加载原理解析 2.深入理解Java类加载器(2):线程上下文类加载器

  6. 深入理解JVM之类加载

    ---title: [学习]深入理解JVM之类加载.mddate: 2019-10-20 22:20:06tags: JVM 类加载--- Java类的加载,连接,初始化都是在程序运行期间执行的 ## ...

  7. 1.1 jvm核心类加载器--jdk源码剖析

    目录 前提: 运行环境 1. 类加载的过程 1.1 类加载器初始化的过程 1.2 类加载的过程 1.3 类的懒加载 2. jvm核心类加载器 3. 双亲委派机制 4. 自定义类加载器 5. tomca ...

  8. 深入理解JVM(六)——类加载器原理

    我们知道我们编写的java代码,会经过编译器编译成字节码文件(class文件),再把字节码文件装载到JVM中,映射到各个内存区域中,我们的程序就可以在内存中运行了.那么字节码文件是怎样装载到JVM中的 ...

  9. JVM学习--(六)类加载器原理

    我们知道我们编写的java代码,会经过编译器编译成字节码文件(class文件),再把字节码文件装载到JVM中,映射到各个内存区域中,我们的程序就可以在内存中运行了.那么字节码文件是怎样装载到JVM中的 ...

随机推荐

  1. 【转载】漫谈C++:良好的编程习惯与编程要点

    原文: 漫谈C++:良好的编程习惯与编程要点 阅读目录 以良好的方式编写C++ class Class with pointer member(s):记得写Big Three static与类 正文 ...

  2. Django模型层:多表查询

    一 创建模型 实例:我们来假定下面这些概念,字段和关系 作者模型:一个作者有姓名和年龄. 作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息.作者详情模型和作者模型之间是一对一的关 ...

  3. 二、Django用户认证之cookie和session

    1.cookie原理 Cookie意为“甜饼”,是由W3C组织提出,最早由Netscape社区发展的一种机制.目前Cookie已经成为标准,所有的主流浏览器如IE.Netscape.Firefox.O ...

  4. Android开发笔记——ListView模块、缓存及性能

    ListView是Android开发中最常用的组件之一.本文将重点说明如何正确使用ListView,以及使用过程中可能遇到的问题. ListView开发模块 图片缓存 可能遇到的问题 一.ListVi ...

  5. Appium+python HTML测试报告(1)(转)

    (原文:https://www.cnblogs.com/fancy0158/p/10054632.html) 测试任务执行完成后,我们需要一份通俗易懂的测试报告来展示自动化测试的结果. HTMLTes ...

  6. python yagmail第三方库发送邮件--更简洁

    1.安装第三方库yagmail: pip install yagmail 2.上代码 import yagmail import os def send_email(): #链接邮箱服务器 serve ...

  7. 小白初识 - 计数排序(CountingSort)

    计数排序,属于桶排序特殊的一种. 当要排序n个数据的时候,如果所处的范围不大,我们可以取其中的最大值K,并将数据分散在K个桶里面, 每个桶里面的数据都是相同的(这样省去了桶内排序的时间),然后顺序取出 ...

  8. 【java请求】- jmeter_jdbc脚本实战

    一,导入 使用Jmeter运行Java脚本,需要用到Jmeter的提供的框架jar包(分别在jmeter目录下的lib和ext目录下)1.ApacheJMeter_core.jar2.ApacheJM ...

  9. fetch上传文件报错的问题(multipart: NextPart: EOF)

    技术栈 后台: gin(golang) 前端: react+antd+dva 问题 前端这边使用fetch发送http请求的时候,后端解析formData报错: multipart: NextPart ...

  10. python2和python3同时存在如何安装和使用pip

    linux下 如果没有pip则需要安装pip python2安装pip sudo apt install python-pip1如果是python3,则如下: sudo apt install pyt ...