深入理解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 三种老年代回收器,此外还有…

oracle跨越千年处理

如果指定的两位年份0-4950-99 如果当前 的两位年 份是 0-49返回的日期是当前世纪返回的日期是上个世纪50-99返回的日期是下个世纪返回的日期是当前世纪 current yearSpecified DateRR FormatYY Format199527-OCT-9519951995199527-OCT-171951917200127-OCT-1720012017200127-OC…

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

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

查看表空间相关命令

默认表空间数据文件大小根据DATA BLOCKS的大小有关,默认最大为32GB表空间达到32G,只能增加数据文件alter tablespace 表空间名 add datafile 数据文件路径‘ size 500m autoextend on next 100m maxsize 10000M;未达到32G,修改数据文件的扩展…

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

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

RESETLOGS

使用resetlogs选项,会把当前的日志序号(log sequence number)重设为1,并抛弃所有日志信息。在以下条件时需要使用resetlogs选项: 在不完全恢复(介质恢复); 使用备份控制文件。 使…

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

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

oracle参数文件和口令文件

外部 审核 口令:记录超级用户的用户名和口令,做sys用户的安全审核 oracle9以后全部使用sys登录,但需要使用as sysdba ,之前版本需要使用internal o7字典打开 只要用户和密码存在于口令文件,就可以以sysdba登录&#…

innobackup备份恢复实操步骤--gtid复制(1)(1)

首先在主库进行备份: 备份命令: Innobackupex --defaults-file/app/dbcluster/sgrdb/mysql/my19103.cnf --no-timestamp --userdbscale --passwordS6000dbscale --host10.157.43.224 --port19103 /data/backup 如果使用setsid: setsid …

Redis系列:Redis的概述与安装

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

安装LibreOffice和字体

#/bin/bash # Check if user is root if [ $(id -u) ! "0" ]; thenecho "Error: You must be root to run this script, please use root"exit 1 fi echo 安装LibreOffice cd /home/ tar -zxvf LibreOffice_6.3.3_Linux_x86-64_rpm.tar.gz cd /home/LibreO…

xtrabackup备份脚本

#!/bin/sh #备份主机 remote_ip100.0.132.160 Master_ip100.20.132.158 VIP100.20.132.166 #备份用户 userroot #密码 password00000 # 返回年月日 backup_datedate %F # 返回时分秒 backup_timedate %H-%M-%S # 返回今天是这周的第几天 backup_week_daydate %u backup_ok0 #备…

Redis系列:使用Redis实现分布式锁及相关问题

分布式锁其实就是,控制分布式系统不同进程共同访问共享资源的一种锁的实现。如果不同的系统或同一个系统的不同主机之间共享了某个临界资源,往往需要互斥来防止彼此干扰,以保证一致性。 本篇内容包括:关于 Redis 与 分布式锁&…

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

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

Java基础:Java程序设计环境

按应用范围,Java 可分为 3 个体系,即 Java SE、Java EE 和 Java ME。Java 语言的开发运行,也离不开 Java 语言的运行环境 JRE。没有 JRE 的支持,Java 语言便无法运行。当然,如果还想编译 Java 程序,搞搞小开…

负载均衡策略

轮循均衡(Round Robin):每一次来自网络的请求轮流分配给内部中的服务器,从1至N然后重新开始。此种均衡算法适合于服务器组中的所有服务器都有相同的软硬件配置并且平均服务请求相对均衡的情况。 我们的业务web服务器都是同样配置…

Java基础:Java数据类型

Java 是一种强类型语言,这就意味着必须为每一个变量声明一种类型。在 Java 中基本数据类型共有 8 种,包括 4 种整型、2 种浮点型、1 种用于表现 Unicode 编码的字符单元的字符类型 char 和一种用于表示真值的 boolean 类型 ~ 本篇主要记录内容包括&#…