深入理解Java虚拟机:Java类的加载机制

本篇内容包括:Java 类的加载机制(Jvm 结构组成、Java 类的加载)、类的生命周期(加载-验证-准备-解析-初始化-使用-卸载)、类加载器 以及 双亲委派模型。

一、Java 类的加载机制

1、 Jvm 结构组成

Jvm 整体组成可分为四个部分:类加载器、运行时数据区(Runtime Data Area)、执行引擎(Execution Engine)、本地库接口(Native Interface)

  • 类加载器:负责从字节码(Class)文件中,加载 class 信息到运行时数据区的方法区;
  • 运行时数据区:存放 Jvm 在执行 Java 程序时相关数据的区域;
  • 执行引擎:将字节码翻译成底层系统指令再交由 CPU 去执行;
  • 本地库接口:执行过程中可能需要调用到其他语言(比如 C 语言)的本地接口。

PS:Javac 是收录于 Jdk 中的 Java 语言编译器。该工具可以将后缀名为 .java 的源文件编译为后缀名为 .class 的可以运行于 Java 虚拟机的字节码。

程序在被执行之前, Java 代码会被先转换成字节码(.class 文件), Jvm 首先通过一定的方式类加载器(ClassLoader)把字节码文件加载到内存中运行时数据区(Runtime Data Area),而字节码文件是 Jvm 提供的一套指令集规范,并不能直接交个底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine)将字节码翻译成底层系统指令再交由 CPU 去执行,而这个过程中需要调用其他语言的接口本地库接口(Native Interface)来实现整个程序的功能,这就是这 4 个主要组成部分的职责与功能。

而我们通常所说的 Jvm 组成指的是运行时数据区,因为通常需要程序员调试分析的区域就是运行时数据区,或者更具体的来说就是运行时数据区里面的堆(Heap)模块!

2、Java 类的加载

类的加载指的是将类的 .class 文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个 java.lang.Class 对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的 Class 对象,Class 对象封装了类在方法区内的数据结构,并且向 Java 程序员提供了访问方法区内的数据结构的接口。

类加载器并不需要等到某个类被首次主动使用时再加载它, Jvm 规范允许类加载器在预料某个类将要被使用时就预先加载它,如果在预先加载的过程中遇到了 .class 文件缺失或存在错误,类加载器必须在程序首次主动使用该类时才报告错误( LinkageError 错误)如果这个类一直没有被程序主动使用,那么类加载器就不会报告错误。

加载.class 文件的方式:

  • 从本地系统中直接加载
  • 通过网络下载 .class 文件
  • 从 zip、jar 等归档文件中加载 .class 文件
  • 从专有数据库中提取 .class 文件
  • 将 Java 源文件动态编译为 .class 文件
  • 由其他文件生成

二、Java 类的生命周期

类从被加载到 Jvm 内存中开始到卸载出内存为止,生命周期分为7个阶段:加载-验证-准备-解析-初始化-使用-卸载。(或分为5个阶段,把 验证-准备-解析 分为连接阶段)

1、加载

加载过程就是把 class 字节码文件载入到虚拟机中,至于从哪儿加载,虚拟机设计者并没有限定,你可以从文件、压缩包、网络、数据库等等地方加载 class 字节码。

  • 通过一个类的全限定名来获取其定义的二进制字节流;
  • 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构;
  • 在Java堆中生成一个代表这个类的 java.lang.Class 对象,作为对方法区中这些数据的访问入口

2、验证(连接阶段的第一步):确保被加载的类的正确性

这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。验证阶段大致会完成4个阶段的检验动作:文件格式验证、元数据验证、字节码验证、符号引用验证

验证阶段是非常重要的,但不是必须的,它对程序运行期没有影响,如果所引用的类经过反复验证,那么可以考虑采用 -Xverifynone 参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。

3、准备(连接阶段的第二步):为类的静态变量分配内存,并将其初始化为默认值

准备阶段的工作就是为类的静态变量分配内存并设为 Jvm 默认的初值,对于非静态的变量,则不会为它们分配内存。静态变量的初值为 Jvm 默认的初值,而不是我们在程序中设定的初值。(仅包含类变,不包含实例变量)

4、解析(连接阶段的第三步):把类中的符号引用转换为直接引用

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。符号引用就是一组符号来描述目标,可以是任何字面量。

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

5、初始化:为类的静态变量赋予正确的初始值

主要对类变量进行初始化。在Java中对类变量进行初始值设定有两种方式:

  1. 声明类变量是指定初始值;
  2. 使用静态代码块为类变量指定初始值

Jvm初始化步骤:

  1. 假如这个类还没有被加载和连接,则程序先加载并连接该类

  2. 假如该类的直接父类还没有被初始化,则先初始化其直接父类

  3. 假如类中有初始化语句,则系统依次执行这些初始化语句

类初始化时机:只有当对类的主动使用的时候才会导致类的初始化,类的主动使用包括以下六种:

  1. 创建类的实例,也就是new的方式
  2. 访问某个类或接口的静态变量,或者对该静态变量赋值
  3. 调用类的静态方法
  4. 反射(如 Class.forName(“com.shengsiyuan.Test”) )
  5. 初始化某个类的子类,则其父类也会被初始化
  6. Java虚拟机启动时被标明为启动类的类( JavaTest ),直接使用 java.exe 命令来运行某个主类

三、类加载器

类加载器是负责将可能是网络上、也可能是磁盘上的 .class 文件加载到内存中。并为其生成对应的 java.lang.Class 对象。一旦一个类被载入 Jvm 了,同一个类就不会被再次加载。那么怎样才算是同一个类?在 Java 中一个类用其全限定类名(包名和类名)作为其唯一标识,但是在 Jvm 中,一个类用其全限定类名和其类加载器作为其唯一标识。也就是说,在 Java 中的同一个类,如果用不同的类加载器加载,则生成的 .class 对象认为是不同的。

当 Jvm启动时,会形成由三个类加载器组成的初始类加载器层次结构:

  • 启动类加载器(Bootstrap ClassLoader):是嵌在 Jvm 内核中的加载器,该加载器是用 C++ 语言写的,主要负载加载 JAVA_HOME/lib 下的类库,启动类加载器无法被应用程序直接使用;
  • 扩展类加载器(Extension ClassLoader):该加载器器是用JAVA编写,且它的父类加载器是 Bootstrap,是由 sun.misc.Launcher$ExtClassLoader实现的,主要加载 JAVA_HOME/lib/ext 目录中的类库。开发者可以这几使用扩展类加载器;
  • 系统类加载器(App ClassLoader):也称为应用程序类加载器,负责加载应用程序 classpath 目录下的所有 .jar 和 .class 文件。它的父加载器为 Extension ClassLoader。

上述三种类加载器的层次关系如下:

Ps:类加载器的体系并不是“继承”体系,而是委派体系,大多数类加载器首先会到自己的 parent 中查找类或者资源,如果找不到才会到自己本地查找。类加载器的委托行为动机是为了避免相同的类被加载多次。

五、双亲委派模型

1、双亲委派模型

如果以上三种类加载器不能满足要求的话,程序员还可以自定义类加载器(继承 java.lang.ClassLoader 类)

它们的层级关系即 自定义类加载器 -> 应用程序加载器 -> 扩展加载器 -> 启动类加载器,这种层次关系被称作为双亲委派模型:如果一个类加载器收到了加载类的请求,它会先把请求委托给上层加载器去完成,上层加载器又会委托上上层加载器,一直到最顶层的类加载器;如果上层加载器无法完成类的加载工作时,当前类加载器才会尝试自己去加载这个类。

2、双亲委派模式优势

  • 采用双亲委派模式的是好处是 Java 类随着它的类加载器一起具备了一种带有先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子 ClassLoader 再加载一次;
  • 其次是考虑到安全因素,Java 核心 api 中定义类型不会被随意替换,假设通过网络传递一个名为 java.lang.Integer 的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心 Java API 发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的 java.lang.Integer,而直接返回已加载过的 Integer.class,这样便可以防止核心 API 库被随意篡改;
  • 可能你会想,如果我们在 classpath 路径下自定义一个名为 java.lang.SingleInterge 类呢?该类并不存在 java.lang 中,经过双亲委托模式,传递到启动类加载器中,由于父类加载器路径下并没有该类,所以不会加载,将反向委托给子类加载器加载,最终会通过系统类加载器加载该类。但是这样做是不允许,因为 java.lang 是核心 API 包,需要访问权限,强制加载将会报出异常 java.lang.SecurityException: Prohibited package name: java.lang 所以无论如何都无法加载成功的。

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

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

相关文章

新版谷歌浏览器开启Flash支持

浏览器地址栏中输入chrome://version查看Chrome浏览器、Flash插件的版本信息。 Chrome 69.0-70.0版本Chrome 71.0-74.0及以后版本谷歌浏览器地址栏中输入【chrome://flags/#enable-ephemeral-flash-permission】,将【Enable Ephemeral Flash Permissions】从【Defau…

深入理解Java虚拟机:Java垃圾回收机制

本篇内容包括:JAVA 垃圾回收机制概述、有哪些内存需要回收、如何回收(标记-清除、标记-整理(标记-清除-压缩)、复制(标记-复制-清除)、分代收集等算法) 以及 何时进行垃圾回收等内容&#xff01…

深入理解Java虚拟机:Java垃圾回收器

本篇内容包括:7 种 Jvm 垃圾回收器的介绍、对比 以及 对应的 Jvm 参数设置,这 7 种包括了:Serial、ParNew 以及 Parallel Scavenge 三种新生代回收器 和 :Serial Old、Parallel Old 以及 CMS 三种老年代回收器,此外还有…

网络协议:什么是网络分层的七四五

本篇内容包括:网络分层七层、五层、四层网络协议概念的介绍,IOS 体系结构的介绍与构成、TCP/IP体系结构的简介及与IOS体系的关系 以及五层体系结构的介绍。 一、七层、五层、四层网络协议概念 1、关于网络协议 网络协议,即是指计算机网络中…

网络协议:一文搞懂Socket套接字

本篇内容包括:Socket 套接字的简介、Socket 套接字的分类、Java 中的 Socket 即 java.net.ServerSocket、java.net.Socket 的使用,以及Java 使用套接字 Scoket 编程的Demo。 一、Socket 简介 TCP(传输控制协议)是一种面向连接的、…

网络协议:透彻解析HTTP协议

本篇内容包括:HTTP 协议定义及其特点概述、关于 URL 定义及分类概述、Request 请求、Response 响应 以及 浏览器访问一个网站的全过程 等内容… 一、HTTP 协议概述 HTTP(HyperText Transfer Protocol) 即 超文本传输协议,它是一种…

Redis系列:Redis的概述与安装

Redis(Remote Dictionary Server) 是一个使用 C 语言编写的,开源的(BSD许可)高性能非关系型(NoSQL)的键值对数据库。 本篇内容包括:Redis 简介(为什么快?为什么单线程?优…

Redis系列:Redis持久化机制与Redis事务

Redis 是个基于内存的数据库。那服务一旦宕机,内存中数据必将全部丢失。所以丢失数据的恢复对于 Redis 是十分重要的,我们首先想到是可以从数据库中恢复,但是在由 Redis 宕机时(说明相关工作正在运行)且数据量很大情况…

TCP连接的建立与终止

TCP连接的建立与终止 1.三次握手 TCP是面向连接的,无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接。在TCP/IP协议中,TCP协议提供可靠的连接服务,连接是通过三次握手进行初始化的。三次握手的目的是同步连接双方…

13 张图带你学懂 Kubernetes Service(转载)

在 Kubernetes 中 Service 主要有4种不同的类型,其中的 ClusterIP 是最基础的,如下图所示: 当我们创建一个 NodePort 的 Service 时,它也会创建一个 ClusterIP,而如果你创建一个 LoadBalancer,它就会创建一…

DOCKERFILE参数注解

Dockerfile由一行行命令语句组成,并且支持以#开头的注释行。 一般的,Dockerfile 分为四部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令。 Dockerfile的指令是忽略大小写的,建议使用大写,使用 # 作为…

Zookeeper:分布式过程协同技术

Zookeeper 是一个高性能的分布式一致系统,在分布式系统中有着广泛的应用。基于它,可以实现诸如“分布式同步”、“配置管理”、“命名空间管理”等众多功能,是分布式系统中常见的基础系统。Zookeeper 主要用来解决分布式集群中应用系统的一致…

Linux namespace概述

操作系统通过虚拟内存技术,使得每个用户进程都认为自己拥有所有的物理内存,这是操作系统对内存的虚拟化。操作系统通过分时调度系统,每个进程都能被【公平地】调度执行,即每个进程都能获取到CPU,使得每个进程都认为自己…

Zookeeper:Zookeeper的主从选举机制

ZAB 协议,全称 Zookeeper Atomic Broadcast(Zookeeper 原子广播协议),是为分布式协调服务 ZooKeeper 专门设计的一种支持崩溃恢复的一致性协议。基于该协议,ZooKeeper 实现了一种主从模式的系统架构来保持集群中各个副…

Linux namespace之:uts namespace

理解uts namespace uts(UNIX Time-Sharing System) namespace可隔离hostname和NIS Domain name资源,使得一个宿主机可拥有多个主机名或Domain Name。换句话说,可让不同namespace中的进程看到不同的主机名。 例如,使用unshare命令(较新版本L…

Kubernetes 的原理

kubernetes 已经成为容器编排领域的王者,它是基于容器的集群编排引擎,具备扩展集群、滚动升级回滚、弹性伸缩、自动治愈、服务发现等多种特性能力。 本文将带着大家快速了解 kubernetes ,了解我们谈论 kubernetes 都是在谈论什么。 kuberne…

CPU 是如何执行任务的

前言 你清楚下面这几个问题吗? 有了内存,为什么还需要 CPU Cache? CPU 是怎么读写数据的? 如何让 CPU 能读取数据更快一些? CPU 伪共享是如何发生的?又该如何避免? CPU 是如何调度任务的&a…

Ansible 的自动化运维

1、Ansible 特点 Ansible 自 2012 年发布以来,很快在全球流行,其特点如下: Ansible 基于 Python 开发,运维工程师对其二次开发相对比较容易; Ansible 丰富的内置模块,几乎可以满足一切要求; …

图文并茂 VLAN 详解,让你看一遍就理解 VLAN

一、为什么需要VLAN 1.1、什么是VLAN? VLAN(Virtual LAN),翻译成中文是“虚拟局域网”。LAN可以是由少数几台家用计算机构成的网络,也可以是数以百计的计算机构成的企业网络。VLAN所指的LAN特指使用路由器分割的网络——也就是广播域。 在此让我们先复习…