2021年度最全面JVM虚拟机,类加载过程与类加载器

前言

类装载器子系统是JVM中非常重要的部分,是学习JVM绕不开的一关。

一般来说,Java 类的虚拟机使用 Java 方式如下:

Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。

类加载器负责读取 Java 字节代码,并转换成 java.lang.Class类的一个实例。
每个这样的实例用来表示一个 Java 类。

通过此实例的 newInstance()方法就可以创建出该类的一个对象。

类的生命周期

我们先来看下类的生命周期,包括:

  • 加载

  • 连接

  • 初始化

  • 使用

  • 卸载

其中加载、连接、初始化属于类加载过程。

使用是指我们new对象进行使用。

卸载指对象被GC垃圾回收掉。

类加载过程

JVM的类加载的过程是通过引导类加载器(bootstrap class loader)创建一个初始类(initial class)来完成的,这个类是由JVM的具体实现指定的。

Class 文件需要加载到虚拟机中之后才能运行和使用,系统加载 Class 类型的文件份如下几步:

  • 加载
  • 连接
  • 验证
  • 准备
  • 解析
  • 初始

顺序是这样一个顺序,但是加载阶段和连接阶段的部分内容是交叉进行的,加载阶段尚未结束,连接阶段可能就已经开始了。

下面我们来逐步解析

加载

这里的加载是微观上的,是类加载过程中的一小步,也是第一步,类加载过程中的加载是宏观上的。

加载的流程如下:

  • 通过全类名获取定义此类的二进制字节流

  • 将字节流所代表的静态存储结构转换为方法区的运行时数据结构

  • 在内存中生成一个代表该类的 Class 对象,作为方法区这些数据的访问入口

  • 简单来说就是:加载二进制数据到内存 —> 映射成JVM能识别的结构—> 在内存中生成class文件。

  • 在虚拟机规范上,对这部分的规定并不具体,所以实现方式是很灵活的。

加载阶段我们可以用自定义类加载器去控制字节流的获取方式,是非数组类的可控性最强的阶段,而数组类型不通过类加载器创建,它由 Java 虚拟机直接创建。

关于类加载器是什么,后文再聊。//加入Java开发交流君样:756584822一起吹水聊天

连接

连接分为三步,验证、准备、解析,目的是将上面创建好的Class类合并至JVM中,使之能够执行的过程。

验证

确保class文件中的字节流包含的信息,符合当前虚拟机的要求,保证这个被加载的class类的正确性,不会危害到虚拟机的安全。

准备

为类中的静态字段分配内存,并设置默认的初始值,比如int类型初始值是0。

被final修饰的static字段不会设置,因为final在编译的时候就分配了。

解析

解析阶段的目的,是将常量池内的符号引用转换为直接引用的过程。

解析动作主要针对类、接口、字段、类方法、接口方法、方法类型等。
如果符号引用指向一个未被加载的类,或者未被加载类的字段或方法,那么解析将触发这个类的加载(但未必触发这个类的链接以及初始化。)

符号引用就是一组符号来描述目标,可以是任何字面量。

直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。

举个例子:

在程序执行方法时,系统需要明确知道这个方法所在的位置。

Java 虚拟机为每个类都准备了一张方法表来存放类中所有的方法。

当需要调用一个类的方法的时候,只要知道这个方法在方法表中的偏移量就可以直接调用该方法了。

通过解析操作符号引用就可以直接转变为目标方法在类中方法表的位置,从而使得方法可以被调用。

所以,解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,也就是得到类或者字段、方法在内存中的指针或者偏移量。

初始化

初始化就是执行类的构造器方法,是类加载的最后一步,这一步 JVM才开始真正执行类中定义的 Java 程序代码

这个方法不需要定义,是javac编译器自动收集类中所有类变量的赋值动作和静态代码块中的语句合并来的。

若该类具有父类,jvm会保证父类的init()先执行,然后在执行子类的init()

对于初始化阶段,虚拟机严格规范了有且只有 5 种情况下,必须对类进行初始化,只有主动去使用类才会初始化类:

  • 当遇到 newgetstaticputstaticinvokestatic这 4 条直接码指令时

  • 当遇到一个类,读取一个静态字段(未被final修饰)、或调用一个类的静态方法时。

  • JVM执行new指令时会初始化类。即当程序创建一个类的实例对象。

  • JVM执行 getstatic指令时会初始化类。即程序访问类的静态变量(不是静态常量,常量会被加载到运行时常量池)。

  • JVM执行 putstatic指令时会初始化类。即程序给类的静态变量赋值。

  • JVM执行invokestatic指令时会初始化类。即程序调用类的静态方法。

  • 对类进行反射调用时,如果类没初始化,需要触发其初始化。

初始化一个类,如果其父类还未初始化,则先触发该父类的初始化。

当虚拟机启动时,用户需要定义一个要执行的主类 (包含 main 方法的那个类),虚拟机会先初始化这个类。

MethodHandleVarHandle 可以看作是轻量级的反射调用机制,而要想使用这 2 个调用, 就必须先使用 findStaticVarHandle 来初始化要调用的类。

「补充,来自issue745」 当一个接口中定义了 JDK8 新加入的默认方法(被 default 关键字修饰的接口方法)时,如果有这个接口的实现类发生了初始化,那该接口要在其之前被初始化。

类加载器

三大类加载器

了解了类加载过程后,我们来看看类加载器。

类加载器(ClassLoader)用来加载 Java 类到 Java 虚拟机中。

JVM 中内置了三个重要的 ClassLoader,同时按如下顺序进行加载:

  • BootstrapClassLoader启动类加载器:最顶层的加载类,由C++实现,负责加载 %JAVA_HOME%/lib目录下的核心jar包和类或者或被 -Xbootclasspath参数指定的路径中的所有类。
  • ExtensionClassLoader扩展类加载器:主要负责加载目录%JRE_HOME%/lib/ext目录下的jar包和类,或被 java.ext.dirs 系统变量所指定的路径下的jar包。
  • AppClassLoader 应用程序类加载器:面向我们用户的加载器,负责加载当前应用classpath下的所有jar包和类。
    除了BootstrapClassLoader其他类加载器均由 Java 实现且全部继承自java.lang.ClassLoader

类的加载几乎是由上述3种类加载器相互配合执行的,在必要时,我们还可以自定义类加载器。

需要注意的是,Java虚拟机对Class文件采用的是按需加载的方式,也就是说当需要使用该类时才会将它的Class文件加载到内存生成Class对象。

双亲委派模型

概念

每一个类都有一个对应它的类加载器。在加载类的时候,是采用的双亲委派模型,即把请优求先交给父类处理的一种任务委派模式。

系统中的类加载器在协同工作的时候会默认使用 双亲委派模型 。//加入Java开发交流君样:756584822一起吹水聊天

双亲委派模型的理论很简单,分为如下几步:

  • 即在类加载的时候,系统会首先判断当前类是否被加载过。已经被加载的类会直接返回,否则才会尝试加载。

  • 加载的时候,首先会把该请求委派给该父类加载器的 loadClass()处理,因此所有的请求最终都应该传送到顶层的启动类加载器 BootstrapClassLoader 中。
    当父类加载器无法处理时,才由自己来处理。

  • AppClassLoader的父类加载器为ExtensionClassLoaderExtensionClassLoader 的父类加载器为null,当父类加载器为null时,会使用启动类加载器BootstrapClassLoader 作为父类加载器。

为什么要使用双亲委派模型

试想一种情况,我们在项目目录下,手动创建了一个java.lang 包,并在该包下创建了一个Object,这时候我们再去启动Java程序,原生Object会被篡改吗?当然是不会的!

因为Object类是Java的核心库类,由BootstrapClassLoader加载,而自定义的java.lang.Object类应该是由AppClassLoader来加载。

BootstrapClassLoader先于AppClassLoader进行加载,根据上面的双亲委派模型的概念,我们可以知道,java.lang.Object类已经被加载,并且AppClassLoader要加载类之前都要先给其父类过目,所以自己写的野类是无法撼动核心库类的。

结论

双亲委派模型保证了Java程序的稳定运行,可以避免类的重复加载,也保证了 Java 的核心 API 不被篡改。

源码分析

双亲委派模型的都集中在java.lang.ClassLoader 的 loadClass()中,相关代码如下所示:

private final ClassLoader parent; 
protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// 首先,检查请求的类是否已经被加载过Class<?> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {//父加载器不为空,调用父加载器loadClass()方法处理if (parent != null) {c = parent.loadClass(name, false);} else {//父加载器为空,使用启动类加载器 BootstrapClassLoader 加载c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {//抛出异常说明父类加载器无法完成加载请求}if (c == null) {long t1 = System.nanoTime();//自己尝试加载c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}//加入Java开发交流君样:756584822一起吹水聊天if (resolve) {resolveClass(c);}return c;}}

反双亲委派模型

双亲委派模型是Java默认的,假如我们不想用双亲委派,我们要怎么办呢?
我们可以自定义一个类加载器,除了BootstrapClassLoader其他类加载器均由 Java 实现且全部继承自java.lang.ClassLoader。如果我们要自定义自己的类加载器,很明显需要继承ClassLoader

从上面的源码我们知道,双亲委派模型的都集中在java.lang.ClassLoader 的 loadClass()中,如果想打破双亲委派模型则需要重写 loadClass() 方法。

如果我们不想打破双亲委派模型,就重写 ClassLoader类中的 findClass()方法即可,无法被父类加载器加载的类最终会通过这个方法被加载。
最后,祝大家早日学有所成,拿到满意offer

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/299684.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

做生意最重要的诚信呢??? | 今日最佳

全世界只有3.14 % 的人关注了青少年数学之旅&#xff08;图源网络&#xff0c;侵权删&#xff09;

面试避坑手册之 Java字节流和字符流总结IO流!

从接收输入值说起 在日常的开发应用中&#xff0c;有时候需要直接接收外部设备如键盘等的输入值&#xff0c;而对于这种数据的接收方式&#xff0c;我们一般有三种方法&#xff1a;字节流读取&#xff0c;字符流读取&#xff0c;Scanner 工具类读取。 字节流读取 直接看一个…

这家AI公司用面具破解中国人脸识别系统!微信、支付宝、火车站无一幸免

全世界只有3.14 % 的人关注了青少年数学之旅据外媒报道&#xff0c;一家人工智能公司Kneron用一个特制的3D面具&#xff0c;成功欺骗了包括支付宝和微信在内的诸多人脸识别支付系统&#xff0c;完成了购物支付程序。他们用同样的方式甚至进入了中国的火车站。现如今&#xff0c…

coolite TreeNode NodeClick传id到后台的方法

重点如下&#xff1a; 1 <AjaxEvents>2 <Click OnEvent"PanelTree_Click" >3 <EventMask ShowMask"true" Msg"正在执行,请稍后" />4 <E…

使用JavaScript实现页面选项自动添加行以及删除行 javaweb

2019独角兽企业重金招聘Python工程师标准>>> <% page language"java" import"java.util.*" pageEncoding"UTF-8"%> <% String path request.getContextPath(); String basePath request.getScheme()"://"reques…

C#基础知识之base、this、new、override、abstract梳理

一、Base关键词的几种用法base重要用于OOP的多态上&#xff0c;base 关键字用于在派生类中实现对基类公有或者受保护成员的访问&#xff0c;但是只局限在构造函数、实例方法和实例属性访问器中1、base调用基类构造函数using System;namespace BaseDemo {class Program{static v…

班主任老师推荐这些优质的教育号,建议家长们多阅读!

全世界只有3.14 % 的人关注了青少年数学之旅推荐几个教育类学习号让孩子少走弯路&#xff0c;为孩子成长保驾护航&#xff01;长按二维码&#xff0c;选择【识别图中二维码】关注理想父母 lixiangfumu&#xff08;长按二维码识别关注&#xff09;关注理由&#xff1a;面向家长…

学妹问我Java枚举类与注解,我直接用这个搞定她!

很多人问我学妹长什么样&#xff0c;不多说 上图吧&#xff01; 学妹问我Java枚举类与注解&#xff0c;我直接一篇文章搞定&#xff01;一、枚举类① 自定义枚举类② enum关键字定义枚举类③ enum 枚举类的方法④ enum 枚举类实现接口二、注解① 生成文档相关注解②注解在编译…

设计模式之代理

代理模式介绍啥是代理模式&#xff1f;代理模式 是一种结构型设计模式&#xff0c;让你能够提供对象的替代品或其占位符。代理控制着对于原对象的访问&#xff0c;并允许在将请求提交给对象前后进行一些处理。其实说通俗点&#xff0c;就好比我们平时生活中的购买机票&#xff…

轮子,辛苦你了。 | 今日最佳

全世界只有3.14 % 的人关注了青少年数学之旅&#xff08;图源帅哥李坏的朋友圈&#xff0c;侵权删&#xff09;

这次牛逼了,面试字节被问LinkedList原理了!手足无措啊

概述 LinkedList底层是基于链表实现。链表没有长度限制&#xff0c;内存地址不需要固定长度&#xff0c;也不需要是连续的地址来进行存储&#xff0c;只需要通过引用来关联前后元素即可完成整个链表的连续。所以链表的优点就是添加删除元素比较快&#xff0c;只需要移动指针&a…

[WP8.1UI控件编程]Windows Phone自定义布局规则

3.2 自定义布局规则 上一节介绍了Windows Phone的系统布局面板和布局系统的相关原理&#xff0c;那么系统的布局面板并不一定会满足所有的你想要实现的布局规律&#xff0c;如果有一些特殊的布局规律&#xff0c;系统的布局面板是不支持&#xff0c;这时候就需要去自定义实现一…

聊聊编程语言的选择

我适合学什么编程语言呢&#xff1f;大家好&#xff0c;我是鱼皮&#xff0c;今天聊聊编程语言的选择问题&#xff0c;通过对 10 主流编程语言的特点、优劣、应用场景、发展前景等简单分析&#xff0c;希望帮还在迷茫的小伙伴们选择最适合自己的语言去学习。编程语言选择本文大…

IIS6文件权限不对触发了Windows身份认证问题解决方法

今天在iis上调试程序的时候突然发现需要登录: 通过csdn提问得知可能是权限设置有问题于是设置了下internet来宾用户: 结果问题没有解决.后来想想应该不是问题,因为我在自己机器上调试用的是Everyone权限,应该都可以访问. 于是我又去用户管理中重置internet来宾用户密码: 重置In…

C++ Exercises(十五)--排序算法的简单实现

structNode {//队列结点 int data; struct Node* pNext;};classCQueue{//队列类(带头结点&#xff09;public: CQueue(void); ~CQueue(void); bool isEmpty()const;//是否为空 void EnQueue(int num);//入队列 int DeQueue();//出队列 int Front()cons…

朋友圈终于能斗表情包了,会发表情包您就多发点!

全世界只有3.14 % 的人关注了青少年数学之旅今日&#xff0c;微信ios端更新至7.0.9版本&#xff0c;又上了一次微博热搜。版本新增了朋友圈图片评论功能&#xff0c;动态图、静态图均可&#xff0c;点击图片评论即可查看大图&#xff0c;评论图片同步聊天表情包库。各路网友收到…

EF Core 异步编程注意要点

????欢迎点赞 &#xff1a;???? 收藏 ⭐留言 ???? 如有错误敬请指正&#xff0c;赐人玫瑰&#xff0c;手留余香&#xff01;????本文作者&#xff1a;由webmote 原创&#xff0c;????作者格言&#xff1a;生活在于折腾&#xff0c;当你不折腾生活时&#x…

看电影的第一大禁忌 | 今日最佳

全世界只有3.14 % 的人关注了青少年数学之旅&#xff08;图源网络&#xff0c;侵权删&#xff09;

求职华为,被问观察者模式,从没有这种体验!!!

求职华为&#xff0c;被问观察者模式&#xff0c;从没有这种体验&#xff01;&#xff01;&#xff01;模式的定义与特点模式的结构与实现1. 模式的结构2. 模式的实现模式的应用实例模式的应用场景模式的扩展1. Observable类2. Observer 接口[ 观察者模式可以说是非常贴近我们…

SQL2005的配置

最近迷上c#&#xff0c;下午装好了SQL server management studio Express 附加经典的northwind数据库 然后用下面一段代码测试 1usingSystem;2usingSystem.Collections.Generic;3usingSystem.Text;4usingSystem.Data;5usingSystem.Data.Sql;6usingSystem.Data.SqlClient;78name…