深入理解Java类加载机制,再也不用死记硬背了

谈谈”会”的三个层次

《说透分布式事务》中,我举例里说明了会与会的差别。对一门语言的学习,这里谈谈我理解的”会”的三个层次:

深入理解Java类加载机制,再也不用死记硬背了

第一层:了解这门语言的语法、写法,我把它叫做 hello world 级别;

第二层:了解这门语言的优劣势以及它的生态,了解这门语言的能力范围,我把它叫做 应用 级别;

第三层:了解这门语言的底层运行机制,这有利于对程序进行调优,以及当程序遇到了比较罕见的问题时能够从根上分析解决它。我把它叫做 掌握 级别。

在简历上写掌握某种语言的,一般面试官也会问一些很深入原理的问题,个人认为比较合理。自己作为一个15年一线Java开发,自认为有资格把掌握Java写在简历上。今天,我就来聊聊我对双亲委派机制一些理解。

插个题外话,在《高并发下秒杀商品,你必须知道的9个细节》中,有朋友问配图是用什么画的。这里介绍一下自己的经验:

1)思维导图还是processon更加方便:

https://www.processon.com/i/594d313ae4b08b003f2ec84a

2)流程图还是draw.io,这个不推荐在线编辑,慢到怀疑人生。安装版本也是免费的,官网可轻松下载。开头图的框图效果是draw.io的框图有个 Sketch 样式。这个样式很好看,但是不建议用于文献等正式场合。正式场合的图最好方方正正,不要太圆润,粗细均匀。

3)生成曲线图、柱状图这些,还是习惯用excel。

Java类加载机制

首先我们需要思考一件事,我们编写的Java代码,是如何在各种各样的操作系统上运行起来的。

深入理解Java类加载机制,再也不用死记硬背了

Java文件通过Javac编译成class文件,这种中间码被称为字节码。然后由JVM加载字节码。这个过程就称为类加载。

运行时,由解释器将字节码解释为一行行的机器码来执行。在程序运行期间,即时编译器会针对热点代码,将该部分字节码编译成机器码以获取更高的执行效率。在整个运行时,解释器和即时编译器相互配合,使程序几乎能达到和编译型语言几乎一样的执行速度。这个部分交给专业的编译器开发人员来做,咱们本篇不做深入讲解。

到此上面那张图就讲完了,不要问我右上角那两个表情是怎么回事。就是发现编辑的时候竟然可以添加表情,觉得好玩就试试看。

类的生命周期

在详细讲解之前,我们明确一下类加载流程的目的。站在高处去看,就是把一份被javac编译过的文件通过加载,生成某种形式的class文件的数据结构送进内存。程序可以调用这个数据结构来构造出Java对象。这个过程是在运行时进行的,也是Java动态拓展性的根基。

深入理解Java类加载机制,再也不用死记硬背了

上面这张图表现了类的整个生命周期。而类加载呢,只包含了加载、链接和初始化三个阶段。加载只是类加载的第一个环节,两者要注意区分。解析部分是灵活的,它可以在初始化环节之前或者之后进行,实现后期绑定。类加载的其他环节的顺序是不可改变的。

加载

加载是一个读取class文件,将其转化为某种静态数据结构而存储在方法区内,并在堆中生成一个便于用户调用的Java对象的过程。

这里值得注意的是,这个Java文件不一定是本地文件,泛指各种来源的二进制流,比如网络、数据库或者比如动态代理技术这样的即时生成的class文件。

验证

验证的步骤很多,上面的图画得不完全准确。对文件格式的校验其实是发生在加载阶段的。通过才能顺利加载。顺利加载并不代表JVM完全认可了这个类,还要进行语法和语义上的分析,保证这个类不会危害JVM,这是对元数据和字节码上的验证。在解析阶段,还会进行符号引用的验证。随着JVM版本的升高,验证过程也在被不断丰富。

准备

准备就是为静态变量赋初始值,注意这里的初始值是JVM默认初始值,是固定的,不是咱们写代码时的那个初始值。这里有个比较容易混淆的概念。

Java内存规范定义了方法区这种抽象概念。主流的JVM实现如HotSpot在JDK8之前使用永久代这种在堆中开辟专门空间的实现方式,JDK8之后使用元空间这种直接内存取代。

HotSpot的实现:类的元信息、常量池、静态变量等都存在在JDK8之前都存在在永久代这种方法区的具体实现中。JDK8之后,常量池、静态变量被从方法区移除,转移到了堆中。元信息这些依然保留在方法区,具体的存储方式改成了元空间。

解析

解析是将符号引用替换为直接引用。

静态解析

符号引用就是假如类A引用了类B,加载阶段是静态解析,这时候B还没有被放到JVM内存中,这时候A引用的只是代表B的符号,这是符号引用。

直接引用就是类A在解析阶段发现自己引用了B,如果这个时候B还没被加载。就是直接触发B的类加载,之后B的符号引用会被替换成实际地址。这被称为直接引用。

动态解析

本文类的生命周期部分引出了后期绑定这个概念。后期绑定其实就是动态解析。如果代码使用了多态。B是一个抽象类或者接口,A就不能知道究竟要用哪个来替换,只能等到实际发生调动时在进行实际地址的替换。这就是为什么有的解析发生在初始化之后。

总结

类加载的过程今天就讲这些。咱们来回顾一下类加载的五个阶段。

深入理解Java类加载机制,再也不用死记硬背了

从JVM的角度看,加载的读取二进制流和初始化阶段,是开放了主导权给用户的。用户可以使用动态代理等手段选择是否这个阶段进行加载。还可以使用多态的手段选择是否在这个阶段进行初始化。而剩下的所有部分都是JVM内部完成的。

此时你可以闭上眼睛回顾一下类加载的五个阶段,是不是不用死记硬背也能了然于胸了。

Original: https://www.cnblogs.com/xiexj/p/15949745.html
Author: 编程一生
Title: 深入理解Java类加载机制,再也不用死记硬背了

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/610710/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 附028.Kubernetes_v1.20.0高可用部署架构二

    kubeadm介绍 kubeadm概述 kubeadm功能 本方案描述 部署规划 节点规划 初始准备 互信配置 其他准备 集群部署 相关组件包 正式安装 部署高可用组件I Keep…

    Linux 2023年6月13日
    0205
  • 重写并自定义依赖的原生的Bean方法

    转载请注明出处: 在项目开发过程中,往往是直接应用很多jar包中依赖且声明好的Bean,拿来即用,但很多场景也需要对这些原生的Bean 进行自定义,定制化封装,这样在项目使用的过程…

    Linux 2023年6月15日
    0128
  • docker inspect 使用

    获取容器 IP 信息 docker inspect -f {{.NetworkSettings.IPAddress}} centos1 获取容器占用overlay2目录 docke…

    Linux 2023年6月6日
    0112
  • 项目管理中的关键路径法-时窗图解法cpm

    完成单个活动所需的时间称为活动时间,可以形象地以一个矩形窗格来表示,这个窗格称为 时间窗口,简称 时窗。 1.1 分类 单位时窗: 基本时窗,时窗的不可分割的最小单元, 活动时窗:…

    Linux 2023年6月13日
    098
  • AOP+Redis+SpringCache翻译字典状态

    1,字典表Or枚举类? 项目里有很多标识状态的字段,比如订单状态:0-未支付,1-已支付,2-已取消。或者性别sex: 0-未知,1-男,2-女 。等等。一般这种我们都会建相应的枚…

    Linux 2023年5月28日
    0119
  • Redis集群-Cluster模式

    1.为什么需要分片集群?它解决了什么问题? Redis从单个节点的架构方式演变到主从模式,提高了数据的可靠性以及相对较弱的高可用性;从主从模式到哨兵模式将高可用性提升到了一个新的高…

    Linux 2023年6月7日
    0116
  • Linux 内核软死锁(soft lockup)bug原因分析

    由日志我们看到,CPU一直处于超负载运行,因此导致机器产生断链的告警 解决方法: echo 30 > /proc/sys/kernel/watchdog_thresh sys…

    Linux 2023年6月13日
    0104
  • SharePoint 2010开发工具图解系列:PowerShell脚本

    练习 :使用PowerShell脚本 在此次练习中,您将了解到如何使用PowerShell和专为SharePoint 2010构建的PowerShell加载项。 从Windows …

    Linux 2023年5月28日
    0100
  • 在cmd中使用doskey来实现alias别名功能

    作为一枚网络工程师,经常就是面对一堆黑框框,也是就是终端。不同操作系统、不同厂家的目录,功能相同但是键入的命令又大不相同,这些差异化容易让脑子混乱。比如华为、思科、H3C、锐捷的设…

    Linux 2023年6月6日
    090
  • mysql update语句的执行流程是怎样的

    update更新语句流程是怎么样的 update更新语句基本流程也会查询select流程一样,都会走一遍。 update涉及更新数据,会对行加dml写锁,这个DML读锁是互斥的。其…

    Linux 2023年6月8日
    099
  • VMware 虚拟机图文安装和配置 Rocky Linux 8.5 教程

    前言这是《VMware 虚拟机图文安装和配置 AlmaLinux OS 8.6 教程》一文的姐妹篇教程,如果你需要阅读它,请点击这里。2020 年,CentOS 宣布:计划未来将重…

    Linux 2023年6月7日
    0228
  • Linux下侦听端口被占用,怎么解决?

    不知道你有没有遇到过这种问题:在同一台物理机器上,服务A 启动时侦听 端口1 ,同时它也作为客户端去连接 服务B,连接服务B时候会随机一个端口号,假如随机的是 端口2 ,这个时候 …

    Linux 2023年6月13日
    0100
  • CentOS安装Docker-ce并配置国内镜像

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Linux 2023年6月7日
    0104
  • 树莓派Raspiberry 编译Linux实时内核PREEMPT-RT 实战

    树莓派4B 实时内核(Preempt_RT)的配置和编译https://blog.csdn.net/zlp_zky/article/details/114994444 基本按照这个…

    Linux 2023年6月7日
    0115
  • DirectX 使用 Vortice 从零开始控制台创建 Direct2D1 窗口修改颜色

    本文将告诉大家如何使用 Vortice 底层库从零开始,从一个控制台项目,开始搭建一个最简单的使用 Direct2D1 的 DirectX 应用。本文属于入门级博客,期望本文能让大…

    Linux 2023年6月6日
    095
  • Teleport&Suspense

    vue3 新添加了一个默认的组件就叫 Teleport,我们可以拿过来直接使用,它上面有一个 to 的属性,它接受一个css query selector 作为参数,这就是代表要把…

    Linux 2023年6月13日
    0117
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球