怎么加载文件_Java虚拟机从入门到入土之JVM的类加载机制

作者:六脉神剑

转载于:https://juejin.im/post/5e1aaf626fb9a0301d11ac8e

JVM总体概述

JVM总体上是由

  • 类装载子系统(ClassLoader)
  • 运行时数据区
  • 执行引擎
  • 内存回收
  • 类文件结构

以上5个部分组成,每一个都是非常重要的,如果你要了解JVM,要学习JVM调优,那么只能是一个个去把他们啃了

什么是类加载机制

书上的原话:

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

类加载的时机

类从被加载到虚拟机内存中开始,到卸载出内存为止,它的生命周期包括

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

总共是7个阶段

dc6f1e683b73ca052f2cba2dd0f0eaad.png

理解类加载三个字

首先 类 是指的.Class文件类,那么怎么生成这个文件呢?

  • Java代码编译
  • 原本就是.Class 文件
  • 动态代理生成

等等 还有很多

那么 加载 这2个字应该怎么理解呢 大家可以看下图

cb2efa71fceb3edea4ce537157c8209a.png

本地的.Class文件通过类加载器加载到JVM内存中的方法区里面,然后通过这个对象来访问数据区的数据

  1. 通过一个类的全限定名来获取定义此类的二进制字节流
  2. 将这个字节流代表的静态存储结构转化成方法区的二进制字节流
  3. 再内存的方法区生成这个类的Java.lang.Class对象,作为这个类各个数据访问的入口

五种必须初始化的情况

Java并没用规定生命时候进行类加载的第一阶段,但是对于初始化阶段,虚拟机有严格的规范

  • 遇到new 关键字的时候
  • 使用reflect包的方法的时候
  • 当初始化一个类的时候发现父类还没初始化,必须先初始化父类
  • 当虚拟机启动的时候,加载main方法的类
  • 当使用1.7的动态语言支持的时候(这块没有接触过,有没有大佬懂的)

验证阶段

分为以下几种样装情况

  • 文件格式的验证,验证当前字节流是否能被JVM识别
  • 元数据的验证,验证它的父类,它的继承,是否是抽象类等
  • 字节码验证,验证逻辑是否合理
  • 符合引用的验证 验证是否能通过生成的Class对象找到对应的数据

准备阶段

准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中分配。对于该阶段有以下几点需要注意:

1、这时候进行内存分配的仅包括类变量(static),而不包括实例变量,实例变量会在对象实例化时随着对象一块分配在Java堆中。

2、这里所设置的初始值通常情况下是数据类型默认的零值(如0、0L、null、false等),而不是被在Java代码中被显式地赋予的值。

假设一个类变量的定义为:

public static int value = 3;

那么变量value在准备阶段过后的初始值为0,而不是3,因为这时候尚未开始执行任何Java方法,而把value赋值为3的putstatic指令是在程序编译后,存放于类构造器()方法之中的,所以把value赋值为3的动作将在初始化阶段才会执行。

下表列出了Java中所有基本数据类型以及reference类型的默认零值:

c4963aa6c875f74bec3d0b99cca89ac7.png

这里还需要注意如下几点:

  • 对基本数据类型来说,对于类变量(static)和全局变量,如果不显式地对其赋值而直接使用,则系统会为其赋予默认的零值,而- 对于局部变量来说,在使用前必须显式地为其赋值,否则编译时不通过。
  • 对于同时被static和final修饰的常量,必须在声明的时候就为其显式地赋值,否则编译时不通过;而只被final修饰的常量则既可以在声明时显式地为其赋值,也可以在类初始化时显式地为其赋值,总之,在使用前必须为其显式地赋值,系统不会为其赋予默认零值。
  • 对于引用数据类型reference来说,如数组引用、对象引用等,如果没有对其进行显式地赋值而直接使用,系统都会为其赋予默认的零值,即null。
  • 如果在数组初始化时没有对数组中的各元素赋值,那么其中的元素将根据对应的数据类型而被赋予默认的零值。

如果类字段的字段属性表中存在ConstantValue属性,即同时被final和static修饰,那么在准备阶段变量value就会被初始化为ConstValue属性所指定的值。

解析阶段

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程 ##初始化阶段 到了初始化阶段,才真正开始执行类中定义的Java代码

类加载器

站在Java虚拟机的角度来讲,只存在两种不同的类加载器:

  • 启动类加载器:它使用C++实现(这里仅限于Hotspot,也就是JDK1.5之后默认的虚拟机,有很多其他的虚拟机是用Java语言实现的),是虚拟机自身的一部分。
  • 所有其他的类加载器:这些类加载器都由Java语言实现,独立于虚拟机之外,并且全部继承自抽象类java.lang.ClassLoader,这些类加载器需要由启动类加载器加载到内存中之后才能去加载其他的类。

站在Java开发人员的角度来看,类加载器可以大致划分为以下四类:

  • 启动类加载器 (C实现)
  • 扩展类加载器 (ClassLoader)
  • 应用程序加载器 (ClassLoader)
  • 自定义加载器 (ClassLoader)

这几种类加载器的层次关系如下图所示:

59ffe8c111577235eb89d3ab320676c8.png

双亲委派模型

类加载器之间的这种层次关系叫做双亲委派模型。 双亲委派模型要求除了顶层的启动类加载器(Bootstrap ClassLoader)外,其余的类加载器都应当有自己的父类加载器。这里的类加载器之间的父子关系一般不是以继承关系实现的,而是用组合实现的。

双亲委派模型的工作过程

由我来概况就是 八个字 向上检查,从下加载 如果一个类接受到类加载请求,他自己不会去加载这个请求,而是将这个类加载请求委派给父类加载器,这样一层一层传送,直到到达启动类加载器(Bootstrap ClassLoader)。 只有当父类加载器无法加载这个请求时,子加载器才会尝试自己去加载。

双亲委派模型的代码实现

双亲委派模型的代码实现集中在java.lang.ClassLoader的loadClass()方法当中。

  • 首先检查类是否被加载,没有则调用父类加载器的loadClass()方法;
  • 若父类加载器为空,则默认使用启动类加载器作为父加载器;
  • 若父类加载失败,抛出ClassNotFoundException 异常后,再调用自己的findClass() 方法。 loadClass源代码如下:
protected synchronized Class> loadClass(String name, boolean resolve) throws ClassNotFoundException {    //1 首先检查类是否被加载    Class c = findLoadedClass(name);    if (c == null) {        try {            if (parent != null) {             //2 没有则调用父类加载器的loadClass()方法;                c = parent.loadClass(name, false);            } else {            //3 若父类加载器为空,则默认使用启动类加载器作为父加载器;                c = findBootstrapClass0(name);            }        } catch (ClassNotFoundException e) {           //4 若父类加载失败,抛出ClassNotFoundException 异常后,这个方法就是加载的核心代码            c = findClass(name);        }    }    if (resolve) {        //5 再调用自己的findClass() 方法。        resolveClass(c);    }    return c;}

自定义类加载器

    class NetworkClassLoader extends ClassLoader { *         String host; *         int port; * *         public Class findClass(String name) { *             byte[] b = loadClassData(name); *             return defineClass(name, b, 0, b.length); *         } *     private byte[] loadClassData(String name) {          // load the class data from the connection           . . .       }     }

这个就是官方的例子

a0c691f68b716693aa09d1853d12e126.png

破环双亲委派

双亲委派模型很好的解决了各个类加载器加载基础类的统一性问题。即越基础的类由越上层的加载器进行加载。 若加载的基础类中需要回调用户代码,而这时顶层的类加载器无法识别这些用户代码,怎么办呢?这时就需要破坏双亲委派模型了。

java默认的线程上下文类加载器是系统类加载器(AppClassLoader).

// Now create the class loader to use to launch the application       try {           loader = AppClassLoader.getAppClassLoader(extcl);       } catch (IOException e) {           throw new InternalError(       "Could not create application class loader" );       }               // Also set the context class loader for the primordial thread.      Thread.currentThread().setContextClassLoader(loader);    

以上代码摘自sun.misc.Launch的无参构造函数Launch()。

使用线程上下文类加载器,可以在执行线程中,抛弃双亲委派加载链模式,使用线程上下文里的类加载器加载类.

典型的例子有,通过线程上下文来加载第三方库jndi实现,而不依赖于双亲委派.

大部分java app服务器(jboss, tomcat..)也是采用contextClassLoader来处理web服务。

a506c532fc724bcc6dabcb59cfed6001.png

结尾

今天把类加载机制好好讲了一下,这样大家就更加的熟悉了内的加载过程,对于Java开发是有好处的

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

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

相关文章

的向上取整函数_计算机二级Excel常用函数解析

决定为大家推点干货让大家学习一下Excel的函数应用ABS绝对值 从最简单的开始第一个是ABS函数简而言之就是取绝对值作用就是MAX&MIN函数 这两个函数是好哥们也比较简单的就是从一堆数字中选出最大值和最小值如图C6格所示ROUND函数 四舍五入函数可不是回合ROUND 1算是比较…

(stl排序+检索)大理石在哪

问题: 现有N个大理石,每个大理石上写了一个非负整数、首先把各数从小到大排序;然后回答Q个问题。每个问题问是否有一个大理石写着某个整数x,如果是,还要回答哪个大理石上写着x。排序后的大理石从左到右编号为1~N。 (…

多个 本地仓库_【运维工具】搭建npm私有镜像仓库,天下苦于npm build久矣

​01 前 言当你的研发团队越来越大,或是你无法忍受node超慢的构建时你可以考虑继续读下去,给大家推荐一个基于Verdaccio相对较完整的解决方案。由于环境的原因,我们直接去 http://npmjs.org 下载就不要考虑了,可以将npm config se…

(STL,vector)木块问题

题目: 输入n,得到编号为0~n-1的木块,分别摆放在顺序排列编号为0~n-1的位置。现对这些木块进行操作,操作分为四种。 1、move a onto b:把木块a、b上方的木块放回各自的原位,再把a放到b上; 2、…

地图自定义图标_如何在H5里添加地图导航?这份教程请收藏!

智能手机的出现为我们的生活带来了翻天覆地的改变,比如说衣食住行都有了显著的变化。外卖让就餐更加方便、手机支付也让生活更加便利,地图导航功能更是让大家从此不再迷路,有了手机以后,大家都开始习惯直接用手机搜索目的地&#…

(STL,set)安迪的第一个字典

问题: 输入一个文本,找出所有不同的单词(连续的字母序列),按字典序从小到大输出。单词不区分大小写。 样例输入 Adventures in Disneyland Two blondes were going to Disneyland when they came to a fork in the…

ios 高德获取定位_解决ios11不支持高德地图API定位功能的方法

在 iOS 11 系统上访问JS API定位业务失败怎么解决?苹果新发的 iOS 11 操作系统的一大特性是对 http 形式访问页面的限制变得非常严格(相比iOS 10 和 iOS 9)。高德提供的JS API功能均支持http和https两种协议访问,在iOS 11操作系统上推荐使用https形式访问…

(STL,map)反片语

题目 输入一些单词,找出所有满足如下条件的单词:该单词不能通过字母重排,得到输入文本中的另外一个单词。在判断是否满足条件时,不区分大小写,但输出保留输入中的大小写,按字典序进行排列(所有…

源码 状态机_LLVM学习笔记(1)--初探源码

一直耳闻LLVM相比于GCC:well documented架构灵活,前后端解耦符合龙书的讲解昨天读到了一篇虽然概括却很周到的llvm入门导引陈钦霖:LLVM Pass入门导引​zhuanlan.zhihu.com就沿着其中的提示探索了下LLVM源码,倒也没有想要成为专业的…

n个数的最小公倍数

文章目录1.利用公式法求两个数最小公倍数2.最大公约数求法3.求几个数的最小公倍数4.例题1.利用公式法求两个数最小公倍数 假设现在要求最小公倍数的两个数为x,y,他们的最大公约数为p,最小公倍数为q。则xypq 2.最大公约数求法 int gg(int a,int b){int c;while(b)…

cv mat的shape_将ndarray转换为cv::Mat的最简单方法是什么?

正如kyamagu建议的那样,您可以使用OpenCV的官方python包装器代码,尤其是pyopencv_to和{}。在我一直在为所有依赖项和生成的头文件而挣扎。然而,可以通过将^{}作为lightalchemist did here进行“清理”来降低复杂性,以便只保留必要…

c语言年月日问题思路总结 闰年非闰年每个月份的天数 解决今天是妹子出生的第多少天的问题

1.闰年非闰年每个月份的天数: int year[2][13]{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };经观察发现: a。2月闰年有29天,非闰年28天 b。1、3、5、7、8、10、12月份&#xf…

aop判断方法是否执行成功_判断图中是否有环的三种方法

0、什么是环?在图论中,环(英语:cycle)是一条只有第一个和最后一个顶点重复的非空路径。在有向图中,一个结点经过两种路线到达另一个结点,未必形成环。1、拓扑排序1.1、无向图使用拓扑排序可以判…

reverse()函数反转字符串以及任意类型数组

文章目录reverse用法1.reverse函数反转string2.reverse函数反转字符数组2.自定义reverse函数反转任意类型数组例子&#xff1a;reverse用法 1.reverse函数反转string #include <iostream> #include <algorithm> #include <string> using namespace std;int…

sap运维要做哪些工作_患上腰椎间盘突出,适合做哪些工作?不适合做哪些工作?...

腰椎间盘突出的患者&#xff0c;大多数是年轻人。年轻人生活和工作压力比较大&#xff0c;大多数人都不可能因为腰椎病完全停止工作&#xff0c;事实上也不用完全停止工作&#xff0c;我们更多地应该虑如何平衡养病和工作之间的关系&#xff0c;那我们今天就来和大家讲讲&#…

(STL,map,queue)团体队列

目录 目录题目&#xff1a;分析与解答1.队列先进先出&#xff0c;正好符合排队问题&#xff0c;所以用队列模拟2.每一个团队有一个队列&#xff0c;团队整体又形成一个队列3.每一个团队的成员和团队编号需要对应&#xff0c;因此利用map存编号为x的人所在的团队编号4.插入队&am…

bat执行exe程序_dos命令start教程,并行运行exe程序或者启动bat批处理cmd脚本

大家好&#xff0c;我是老盖&#xff0c;首先感谢观看本文&#xff0c;本篇文章做的有视频&#xff0c;视频讲述的比较详细&#xff0c;也可以看我发布的视频。今天我们学习DOS命令start这个命令&#xff0c;它可以启动一个EXE程序&#xff0c;也可以启动一个BAT批处理脚本&…

(STL,set,priority_queue)丑数

题目&#xff1a; 丑数是指不能被2,3,5以外的其他素数整除的数。把丑数从小到大排列起来&#xff0c;结果如下&#xff1a;1,2,3,4,5,6,8,9,10,12,…求第1500个丑数 分析与解答&#xff1a; 0.对于任意丑数x&#xff1a;2x&#xff0c;3x&#xff0c;5x也是丑数 1.用优先队…

数据库备份mysql_MySQL数据库备份与恢复方法

常有新手问我该怎么备份数据库&#xff0c;下面介绍3种备份数据库的方法&#xff1a;(1)备份数据库文件MySQL中的每一个数据库和数据表分别对应文件系统中的目录和其下的文件。在Linux下数据库文件的存放目录一般为/var/lib/mysql。在Windows下这个目录视MySQL的安装路径而定&a…

(大整数类Biginteger)大斐波数

题目 Fibonacci数列&#xff0c;定义如下&#xff1a; f(1)f(2)1 f(n)f(n-1)f(n-2) n>3。 计算第n项Fibonacci数值。 输入 输入第一行为一个整数N&#xff0c;接下来N行为整数Pi&#xff08;1<Pi<1000&#xff09;。 输出 输出为N行&#xff0c;每行为对应的…