通俗讲解JVM的类加载机制
作者:H.U.C-王子 发布时间:2023-01-03 03:35:03
前言
我们很多小伙伴平时都是做JAVA开发的,那么作为一名合格的工程师,你是否有仔细的思考过JVM的运行原理呢。
如果懂得了JVM的运行原理和内存模型,像是一些JVM调优、垃圾回收机制等等的问题我们才能有一个更清晰的概念。
为了走进JVM,深入了解底层,王子打算写一个JVM的专题,留下自己对JVM探索的足迹,同时也希望能帮到小伙伴们更好的理解JVM。
那我们开始吧。
JAVA代码的运行流程
首先我们就来聊一聊JAVA代码是怎么运行起来的,这部分比较基础相信大家都知道,就当成是个复习吧。
我们编写的代码都是在java文件中编写的,然后会编译成class字节码文件。
当我们使用到哪个类的时候就会通过类加载器把class字节码文件中的类加载到jvm内存中,然后就是在jvm内存中运行我们的代码了。
整体的运行流程就是这样,相信小伙伴们都很清楚这些,但是有关类加载器是如何把类加载到jvm内存中的,小伙伴们有考虑过吗?
今天我们主要就是聊这一部分。
JVM什么时候加载类
其实说到类加载的底层机制,这是一个很复杂的过程,但是对于我们平时的工作来讲,只要懂得它的核心原理就可以了。
一个类的加载过程会经历如下的几个过程:
加载、验证、准备、解析、初始化、使用、卸载
首先我们就先弄明白一个问题,jvm是什么时候去加载类的呢?
其实答案很简单,就是我们什么时候使用到了这个类,它就去class字节码文件中去加载这个类。
而作为程序的入口,具有main方法的类,肯定是最开始的时候就加载到jvm中了。
对于加载类的时间点问题,其实就是这么简单。
类加载器和双亲委派机制
既然我们知道了类加载的时间点,那么jvm是通过什么方式对类进行加载的呢?就是类加载器。
那接下来我们就来聊聊jvm的类加载器。
jvm的类加载器总体上可以分成4层,我们一起看一下。
1.启动类加载器
首先就是jvm启动的第一道关口,启动类加载器Bootstrap ClassLoader,它主要是加载java的核心类。
相信大家都知道,无论是什么环节下运行java程序,都是要安装jvm虚拟机环境的,而在这个环境的目录中是有一个lib文件夹的,这个文件下就是java最核心的类库,支撑着java系统的运行。
所以一旦jvm启动,那么首先就会通过启动类加载器去加载lib文件夹下的核心类库。
2.扩展类加载器
然后我们就到了第二层,扩展类加载器Extension ClassLoader,这个类加载器其实与启动类加载器是类似的。
在我们的jvm虚拟机环境目录下,是有一个lib/ext的文件夹的,这里面的类就是java运行环境的一些扩展类,这些扩展类就是在jvm启动后,通过扩展类加载器进行加载的。
3.应用程序类加载器
加载完核心类库和扩展类,这时候就到了第三层,应用程序类加载器Application ClassLoader,这个类加载器你就可以理解成是加载我们写好的java代码的就可以了。
4.自定义类加载器
前面的三层就是基本的类加载器了,然后第四层是自定义类加载器,根据一些特殊的需求来自己定义类加载器加载我们的类。
整体上类加载器就是这么的4层结构。很多小伙伴可能都听说过双亲委派机制,那么什么是双亲委派机制呢,王子就和大家用最接地气的语言描述一下。
其实很好理解,就是当我们的类加载器要加载一个类的时候,它首先会委派给它的父亲去加载,但是如果它的父亲没找到就会把这个事交给他的孩子自己去完成了。
这就是双亲委派机制。
举个例子,假如我们的应用程序类加载器要加载一个类A,那么首先它会先回家找它老爸扩展类加载器,问问“老爸,你那有这个类A吗?”
然后扩展类加载器接到这个请求之后,同样也懒得处理,再去找它爷爷启动类加载器。
它爷爷找了一圈没找到类A,很生气,就对扩展类加载器说,“我这没有,你自己找去!”
然后扩展类加载器就灰溜溜的自己找了一圈,同样也没找到,这时候就找到应用类加载器了,说:“我这哪有你这个类A,这明明是你自己应该干的活,爱上哪找上哪找去,我不管了”。
这时候应用类加载器就只能自己去处理了,找了一圈发现找到了类A,就把它加载到jvm内存中了。
相信大家看了这个例子应该很容易理解了吧。
所以假设我们自己创建了一个类java.lang.String,它是不会被应用类加载器加载到内存中的,因为父类中可以找到这个类,就直接给加载到内存中了。
聊聊验证、准备、解析、初始化阶段
聊完了加载,我们再来看看验证、准备、解析、初始化这几个阶段jvm都做了什么。
1.验证阶段
这一步其实很容易理解,就是jvm根据java规范,来校验你加载进来的class文件中的内容是否符合规范,如果不符合规范jvm是无法正常运行的。
所以在加载后,首先就是验证阶段。
2.准备阶段
假设我们有一个类A,刚刚加载并通过了验证,那么就会进行准备工作。
这个准备工作其实就是给类A分配一定的内存空间,然后给里面的静态变量(static修饰的变量)也分配内存空间,并赋初始值。
3.解析阶段
这个阶段干的事实际上是把符号引用替换为直接引用,这一过程网上有很多资料,还是比较复杂的,如果感兴趣小伙伴们可以自己查阅一下资料。
实际工作中也很少会接触这部分的内容,所以我们知道有这么个阶段就可以了。
4.初始化阶段
在准备阶段,我们把类A的内存已经分配完了,那么初始化阶段要做些什么事呢?我们先看一下类A的代码
public class A {
private static String i=System.getProperty("i");
}
准备阶段我们只是给变量i分配了内存空间,并赋值了初始值,但是后边的System.getProperty("i")是不会执行的。
没错,这部分代码就是在初始化阶段执行的,另外静态代码块也会在这一阶段执行。
举个例子,比如我们新建一个对象new A(),此时就会触发从加载到初始化的全过程,把这个类准备好并创建一个实例对象。
此外这里有一个规则,如果类A继承了类B,那么在初始化类A的时候,如果发现类B还没有初始化,会先初始化类B。
扩展
到这里关于JVM的类加载机制其实就已经说完了,王子再给大家扩展一个小知识点。
小伙伴们想过没有,Tomcat也是用java开发的,那么它的类加载机制是什么样的呢,为什么就能支持jsp呢?
其实它就是利用了自定义类加载器这一机制,自己自定义了很多类加载器,整体的结构如下:
Tomcat自定义了这么多的类加载器,用来加载它自己的核心类库,并且Tomcat是打破了双亲委派机制的,感兴趣的小伙伴可以自己去查资料了解一下,王子就不在本篇文章长篇大论来聊Tomcat了。
总结
今天我们聊的内容还是jvm中比较基础的部分,以后的文章我们再慢慢深入,去探索jvm的底层原理,如果对JVM感兴趣的小伙伴可以关注王子的后续文章哦,我们可以一步一个脚印的逐步分解JVM,去了解JVM的垃圾回收机制、性能调优等等实用性问题,让你面对JVM的面试或者生产实践也可以游刃有余。
那我们下次见。
来源:https://www.cnblogs.com/lm970585581/p/13739366.html


猜你喜欢
- 概述从今天开始, 小白我将带大家开启 Java 数据结构 & 算法的新篇章.贪心算法贪心算法 (Greedy Algorithm)
- 本文实例总结了MFC程序设计常用技巧。分享给大家供大家参考。具体如下:1.属性页的添加:创建对话框的类,该类要从CpropertyPage继
- 先看看效果图:1、XML布局引入<com.net168.lib.SortTabLayout android:id=&quo
- 概览阿里巴巴在2018年7月份发布Nacos, Nacos是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。并表示在6-8个
- activity动画方式在AndroidMenifest中添加activity的动画属性windowAnimationStyle <i
- 前言现在很多的 App 都有自动轮播的 banner 界面,用于展示广告图片或者显示当前比较热门的一些活动,除了具备比较酷炫的效果之外,通过
- 经典排序算法 - 基数排序Radix sort原理类似桶排序,这里总是需要10个桶,多次使用首先以个位数的值进行装桶,即个位数为1则放入1号
- Java常用类包装类由于Java语言中的基本类型不是面向对象,并不具备对象的性质,实际使用存在很多不便。Java在java.lang包中提供
- 在上一篇文章中,我们实现了统计每个产品和地区的销售额,如果现在需要统计每个产品和地区所占市场份额的百分比,那么使用堆叠条形图是不合适的,我们
- maven3 安装:安装 Maven 之前要求先确定你的 JDK 已经安装配置完成。Maven是 Apache 下的一个项目,目前最新版本是
- 实现上位机和下位机之间的通信,通常使用的是串口通信,接下来实现一个通过上位机和串口调试助手来完成串口通信测试。首先创建一个WInfrom窗体
- 以String转Date为例:定义转换器:import java.text.ParseException;import java.util.
- 概述LruCache的核心原理就是对LinkedHashMap的有效利用,它的内部存在一个LinkedHashMap成员变量,值得注意的4个
- FileStream,顾名思义,文件流。流,是字节流。我的理解是,硬盘上存在一个字节流,内存里也有一个字节流,它们是对应的。程序运行时,我们
- Maven的安装安装Maven之前要确保已经安装好了jdk,并且配置好了环境变量JAVA_HOME。具体安装步骤如下:1. 从ap
- 背景:最近需要用到人脸识别,但又不花钱使用现有的第三方人脸识别接口,为此使用opencv结合java进行人脸识别(ps:opencv是开源的
- 如何使用 Jetpack Compose 创建翻转卡片效果介绍在电子商务和银行应用程序中输入卡信息是很常见的情况。我认为让用户更轻松地处理这
- 前言通过前面这篇文章Android串口通讯SerialPort的使用详情已经基本掌握了串口的使用,那么不经想问自己,到底什么才是串口通讯呢?
- 在Android开发中,我们经常会需要在Android界面上弹出一些对话框,比如询问用户或者让用户选择。这些功能我们叫它Android Di
- forword跳转页面的三种方式:1.使用serlvet/** * 使用forward跳转,传递基本类型参数到页面