程序员最近都爱上了这个网站  程序员们快来瞅瞅吧!  it98k网:it98k.com

本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长


+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

JVM篇-类加载的过程

发布于2021-06-14 10:23     阅读(1067)     评论(0)     点赞(13)     收藏(2)



类加载机制分为五个部分: 加载 验证 准备 解析 初始化
在这里插入图片描述

类加载过程

回答一

加载:
这个阶段会在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的入口。注意这里不一定非得要从一个Class文件获取,这里既可以从ZIP包中读取(比如从jar包和war包中读取),也可以在运行时计算生成(动态代理),也可以由其它文件生成(比如将JSP文件转换成对应的Class类)。
验证:
这一阶段的主要目的是为了确保Class文件的字节流中包含的信息是否符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
准备:
准备阶段是正式为类变量分配内存并设置类变量的初始值阶段,即在方法区中分配这些变量所使用的内存空间。
解析:
虚拟机将常量池中的符号引用替换为直接引用的过程。
初始化:
前面的类加载阶段之后,除了在加载阶段可以自定义类加载器以外,其它操作都由JVM主导。到了初始阶段,才开始真正执行类中定义的Java程序代码。

回答二

在这里插入图片描述
多个java文件经过编译打包生成可运行jar包,最终由java命令运行某个主类的main函数启动程序,这里首先需要通过类加载器把主类加载到JVM
主类在运行过程中如果使用到其它类,会逐步加载这些类。注意,jar包里的类不是一次性全部加载的,是使用到时才加载。

类加载到使用整个过程有如下几步:加载 >> 验证 >> 准备 >> 解析 >> 初始化 >> 使用 >> 卸载
加载:在硬盘上查找并通过IO读入字节码文件,使用到类时才会加载,例如调用类的main()方法,new对象等等
验证:校验字节码文件的正确性
准备:给类的静态变量分配内存,并赋予默认值(0,false…)
解析:将符号引用替换为直接引用,该阶段会把一些静态方法(符号引用,比如main()方法)替换为指向数据所存内存的指针或句柄等(直接引用),这是所谓的静态链接过程(类加载期间完成),动态链接是在程序运行期间完成的将符号引用替换为直接引用,下节课会讲到动态链接
初始化:对类的静态变量初始化为指定的值,执行静态代码块

双亲委派机制

加载动作在JVM外部实现的,以便让应用程序决定如何获取所需的类,JVM提供了3种类加载器:
启动类加载器(Bootstrap ClassLoader):负责加载 JAVA_HOME\lib 目录中的,或通过-Xbootclasspath参数指定路径中的,且被虚拟机认可(按文件名识别,如rt.jar)的类。
扩展类加载器(Extension ClassLoader):负责加载 JAVA_HOME\lib\ext 目录中的,或通过java.ext.dirs系统变量指定路径中的类库。
应用程序类加载器(Application ClassLoader):负责加载用户路径(classpath)上的类库。
自定义加载器
JVM通过双亲委派模型进行类的加载,当然我们也可以通过继承java.lang.ClassLoader实现自定义的类加载器。
在这里插入图片描述
当一个类加载器收到类加载任务,会先交给其父类加载器去完成。因此最终加载任务都会传递到顶层的启动类加载器,只有当父类加载器无法完成加载任务时,才会尝试执行加载任务。
采用双亲委派的一个好处是比如加载位于rt.jar包中的类java.lang.Object,不管是哪个加载器加载这个类,最终都是委托给顶层的启动类加载器进行加载,这样就保证了使用不同的类加载器最终得到的都是同样一个Object对象

比如我们的Math类,最先会找应用程序类加载器加载,应用程序类加载器会先委托扩展类加载器加载,扩展类加载器再委托启动类加载器,顶层启动类加载器在自己的类加载路径里找了半天没找到Math类,则向下退回加载Math类的请求,扩展类加载器收到回复就自己加载,在自己的类加载路径里找了半天也没找到Math类,又向下退回Math类的加载请求给应用程序类加载器,应用程序类加载器于是在自己的类加载路径里找Math类,结果找到了就自己加载了。

双亲委派机制说简单点就是,先找父亲加载,不行再由儿子自己加载

双亲委派的作用

防止加载同一个.class。通过委托去询问上级是否已经加载过该.class,如果加载过了,则不需要重新加载。保证了数据安全。
保证核心.class不被篡改。通过委托的方式,保证核心.class不被篡改,即使被篡改也不会被加载,即使被加载也不会是同一个class对象,因为不同的加载器加载同一个.class也不是同一个Class对象。这样则保证了Class的执行安全。

双亲委派机制是必须的吗?

显然不是。这个机制不是强制的,甚至有时候不得不去破坏他。
双亲委派机制出现之前,就有很多用户自定义的类加载器,因此jdk1.2引入机制的时候特地在java.lang.ClassLoader类中增加了一个protected修饰的方法findClass,用户在编写类加载逻辑的时候就去重写这个方法,这样如果类加载失败了,就调用findClass类完成加载。这样既满足用户根据自己的意愿来加载,又可以不违背双亲委派的思想;
②双亲委派本身也有缺陷,它解决了各个类加载器在协作时保证基础类的一致性问题。基础类往往是被用户继承或调用的api,但是也存在基础类要去调用用户代码的情形,比如jdbc、jndi这些服务,他们是由启动类加载器加载的,但是具体的实现是第三方供应商实现的,这些服务的SPI,代码在应用类加载器路径下。这种情形,启动类加载器加载时要调用应用类加载器路径下的东西,按双亲委派机制来的话,是不可行的:由BootstrapClassloader加载的类使用了由AppClassLoader加载的类。
解决:引入线程上下文类加载器,通过Thread类的setContextClassLoader方法设置。比如JNDI服务用它加载需要的SPI服务代码。

部分内容参考博客与书籍。如有侵权,立刻删除。

原文链接:https://blog.csdn.net/luodaxia_ttt/article/details/117756895



所属网站分类: 技术文章 > 博客

作者:java小王子

链接:http://www.javaheidong.com/blog/article/222732/a172cc90d5afa9b5fdee/

来源:java黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

13 0
收藏该文
已收藏

评论内容:(最多支持255个字符)