java(JVM)

JVM

Java的JVM(Java虚拟机)是运行Java程序的关键部件。它不直接理解或执行Java源代码,而是与Java编译器生成的字节码(Bytecode)进行交互。下面是对Java JVM更详尽的解释:

1.字节码

当你使用Java编译器(javac)编译Java源代码时,会生成一种中间语言——字节码(.class文件)。字节码是一种平台无关的代码格式,设计目的是为了实现跨平台的兼容性。

字节码(Bytecode)是一种介于高级编程语言和机器语言之间的低级程序表示形式。它是许多编程语言(如Java、Python等)在编译或解释执行过程中产生的中间代码。字节码的主要特点和作用包括:

  1. 平台无关性:字节码不针对任何特定的硬件架构,它是一种抽象的、与具体处理器无关的指令集。这使得编译后的字节码可以在任何支持该字节码格式的平台上运行,实现了“一次编写,到处运行”的跨平台特性。

  2. 简化编译过程:相比直接生成机器语言,产生字节码的过程更为简单,因为字节码的指令集通常比机器语言指令集更小、更通用。

  3. 安全性增强:在执行字节码前,虚拟机(如Java虚拟机JVM)可以对其进行验证,确保代码的安全性,比如检查类型安全,防止非法访问内存等。

  4. 优化机会:JVM或其他虚拟机可以在运行时对字节码进行动态优化,如即时编译(JIT),将频繁执行的字节码转换为更高效的本地机器码,提高程序执行效率。

  5. 易于分析与变换:由于字节码的结构较为简单且有明确的规范,工具和框架可以更容易地对其进行分析、修改或转换,这对于代码混淆、程序分析、动态代理等技术尤为重要。

例如,在Java中,源代码被编译成.class文件中的字节码,这些字节码随后由Java虚拟机(JVM)解释或即时编译成机器码执行。这一层抽象极大地增强了Java程序的可移植性和安全性。

2.加载与验证

当一个Java程序开始运行时,JVM负责加载所需的字节码文件。加载后,JVM会对这些字节码进行验证,确保它们没有违反Java的安全规范,如类型安全等。

Java 虚拟机(JVM)的类加载过程主要包括五个阶段:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)和初始化(Initialization)。下面是对加载与验证阶段的详细介绍:

加载(Loading)

  1. 寻找并加载类的二进制数据:JVM查找并读取类的字节码文件(通常是.class文件),这个过程可以通过不同的类加载器来完成,包括启动类加载器(Bootstrap ClassLoader)、扩展类加载器(Extension ClassLoader)、系统类加载器(Application ClassLoader)或自定义类加载器。

  2. 创建java.lang.Class实例:JVM为每个加载的类创建一个对应的Class对象,作为方法区中类数据的访问入口。这个对象封装了类的各种信息,如类名、父类、实现的接口、常量池、字段、方法等。

验证(Verification)

验证阶段是确保加载的字节码是否符合JVM规范要求,防止恶意代码损害系统安全。验证过程分为四个主要步骤:

3.文件格式验证

检查.class文件的格式是否正确,包括魔数(Magic Number)、版本号、常量池等,确保文件结构合法。

Java虚拟机(JVM)的文件格式验证是类加载验证过程的第一个阶段,旨在确保.class文件的格式遵守Java Class File Format规范,确保字节码文件的基本结构正确无误,可以被正确解析。此阶段的验证内容主要包括以下几个方面:

  1. 魔数验证(Magic Number Verification):每个.class文件的开头都有一个特殊的4字节序列(十六进制表示为CAFEBABE),被称为魔数。验证器首先检查文件头的魔数是否正确,以此确认文件是否为有效的Java字节码文件。

  2. 版本信息验证:接下来验证.class文件的版本号,包括主版本号和次版本号,确保其与当前JVM的版本兼容。如果不兼容,JVM将拒绝加载该类。

  3. 常量池计数和验证:验证常量池的数量是否与文件中声明的一致,并对常量池中的每一项(如类名、字符串、数字、方法引用等)进行基本的格式验证,确保它们的类型和结构正确。

  4. 访问标志验证:检查类或接口的访问修饰符是否合法,例如确保一个类不能同时被声明为abstractfinal

  5. 类索引、父类索引和接口计数验证:确保类索引指向的常量池项确实代表一个有效的类,父类索引(对于非java.lang.Object的类)指向的是一个有效的超类,接口计数正确无误,且所有接口引用有效。

  6. 字段表和方法表验证:验证类的字段和方法数量是否与声明相符,以及字段和方法的描述符是否合法,包括返回类型、参数类型等。

  7. 属性表验证:检查类、字段和方法的属性表,确保属性的数量、类型和长度正确,特别是对Code属性(包含方法的字节码)的初步检查,以确保其格式合理。

文件格式验证主要是对.class文件的结构进行检查,确保其基本的格式和一致性,是整个类加载验证流程的基础。如果这一阶段检测到任何错误,类加载过程就会终止,并抛出相关错误信息。

4.元数据验证

校验类的元数据信息,如检查类是否有父类(除了java.lang.Object外)、检查final类是否被继承、检查方法的重写是否遵循规则等。

Java虚拟机(JVM)的元数据验证是类加载验证过程的第二个阶段,紧随文件格式验证之后。这一阶段主要关注类结构的语义检查,确保类的元数据信息(如类、接口、字段、方法等)遵循Java语言规范,不会引入逻辑上的冲突或不一致性。元数据验证的具体内容包括但不限于以下几个方面:

  1. 类结构验证:确保类的继承关系合法,例如一个类只能有一个直接父类(除了java.lang.Object外),接口不能有父类,类不能直接或间接实现自己,也不能直接或间接扩展自己。

  2. 字段和方法重载验证:检查类中的字段和方法名称是否唯一(考虑重载的情况下),以及它们的访问权限、修饰符(如staticfinalprivate等)是否合法。

  3. final类和方法验证:确保声明为final的类没有子类,final的方法没有被子类重写。

  4. 抽象类和方法验证:如果一个类声明为抽象类,验证它是否有子类;如果一个方法声明为抽象方法,确保它在非抽象类中不被实现。

  5. 接口验证:确保接口不包含实例字段(除了static final),所有方法都是抽象的(Java 8之后允许默认方法和静态方法),接口不能继承自非接口类型。

  6. 常量池中的符号引用验证:虽然符号引用的完全解析发生在解析阶段,但在元数据验证期间也会对常量池中的某些符号引用进行基本的格式检查,确保它们指向的描述符(如类、方法、字段的描述符)语法上是正确的。

元数据验证是确保类定义逻辑正确性的关键步骤,它帮助JVM识别那些在文件格式上看似正确但实际上违反了Java语言规范的类。通过这一系列严格的检查,JVM能够排除那些在类结构上有潜在问题的类,从而提升系统的稳定性和安全性。

5.字节码验证

这是最复杂的一个阶段,通过数据流分析和控制流分析等手段,确保字节码的语义是合法的,不会引起 JVM 执行时的异常,比如类型转换错误、跳转指令的合法性等。

Java虚拟机(JVM)的字节码验证是类加载验证过程中的第三个阶段,也是最为复杂和关键的一个环节。这一阶段的目标是确保字节码的语义正确性,防止非法或有害的操作,保证程序执行的安全性。字节码验证主要关注以下几个方面:

  1. 类型安全验证:这是字节码验证的核心部分,涉及数据流分析和控制流分析。它检查每个操作码(opcode)对操作数栈和局部变量表的操作是否类型安全,确保不会发生类型不匹配的错误,比如错误的类型转换、非法的运算操作等。例如,确保加法操作的两个操作数都是数值类型,且类型兼容。

  2. 控制流验证:分析字节码的控制流图,确保程序的控制流逻辑是合理的,没有死循环,也不会跳转到不存在的指令或非法的代码段。此外,还检查异常处理表的正确性,确保try-catch块的范围合理且捕获的异常类型与抛出的异常匹配。

  3. 操作数栈和局部变量表的平衡性验证:确保每个方法的执行过程中,操作数栈和局部变量表的使用前后保持平衡,即每条指令执行前后栈深度和局部变量的使用状态符合预期,不会出现栈溢出或下溢的情况。

  4. 方法体验证:详细检查方法内部的代码,包括对方法的Code属性中的字节码指令序列进行验证,确保指令的顺序、分支、跳转逻辑正确,以及对方法返回类型和异常处理的合规性检查。

  5. 对常量池引用的额外验证:在这一阶段,虽然大部分符号引用的解析发生在解析阶段,但对于直接涉及到的常量池条目(如方法调用、字段访问等),会进一步验证这些引用的有效性和类型兼容性。

字节码验证是JVM安全机制的重要组成部分,通过严格的逻辑检查,可以有效防止恶意代码利用字节码层面的漏洞进行攻击,保证了Java程序的健壮性和安全性。尽管这一过程相对耗时,但它对维护Java“一次编写,到处运行”的承诺至关重要。

6.符号引用验证

在解析之前对类自身以外的信息(如常量池中的类、方法、字段符号引用)进行校验,确保可以成功解析到对应的类、方法或字段。

符号引用验证是Java虚拟机(JVM)类加载验证过程中的第四个阶段,发生在解析之前。这一阶段主要关注常量池中的符号引用(Symbolic References),确保这些引用在实际解析时能够成功定位到目标类、字段或方法。符号引用验证包括以下几个方面:

  1. 有效性验证:检查常量池中的符号引用是否格式正确,比如类或接口的全限定名、字段的名称和描述符、方法的名称、描述符及参数类型等,确保这些信息符合Java语言规范。

  2. 可访问性验证:确保当前类对符号引用所指的目标具有合法的访问权限。例如,私有(private)成员不能被外部类访问,包外的类不能访问包内未声明为public的成员等。

  3. 存在性验证:验证被引用的类、字段、方法是否真实存在。虽然这一步骤在实际解析时会更加彻底,但在符号引用验证阶段也会进行初步检查,避免明显的无效引用。

  4. 类型兼容性验证:对于方法调用和字段访问,检查调用者和被调用者之间是否存在兼容性问题,比如方法的参数类型、返回类型与预期是否一致,字段类型是否兼容等。

  5. 接口合法性验证:如果符号引用指向的是接口方法或接口本身,确保符合Java接口的使用规则,比如类实现接口的所有抽象方法,或者接口不能继承自非接口等。

符号引用验证的目的在于提前发现潜在的引用错误,减少在解析阶段因引用问题导致的失败,从而提高类加载的效率和稳定性。虽然解析阶段会进一步验证和具体化这些符号引用,但前期的符号引用验证依然是必要的,它作为一道安全网,有助于维护Java程序的健壮性。

验证阶段是非常重要的,它确保了类文件的格式正确、语义合法,是JVM安全机制的重要组成部分。如果在验证阶段发现错误,JVM将抛出相关异常,阻止有问题的类被加载到内存中执行。

  1. 解释与即时编译(JIT):早期的JVM通过逐行解释字节码来执行程序,这种方式效率较低。现代JVM大多采用即时编译技术(Just-In-Time Compilation),即在运行时将频繁执行的字节码编译为对应平台的本地机器码,从而显著提升执行效率。

  2. 内存管理:JVM自动管理程序运行时的内存分配和回收。它将内存划分为不同的区域,如堆(Heap)用于存储对象实例,栈(Stack)用于方法调用和局部变量,以及方法区(Method Area)用于存储类的元数据等。垃圾收集器(Garbage Collector, GC)是JVM的一部分,负责自动回收不再使用的内存空间,减少程序员手动管理内存的工作。

  3. 多线程支持:JVM内置了对多线程的支持,使得Java程序可以轻松创建和管理多个线程。JVM负责调度线程、管理线程生命周期,并提供了一套内存模型来保证线程间的通信正确性。

  4. 安全性:JVM通过字节码验证、安全沙箱(限制了程序访问本地系统资源的能力)、类加载器体系结构等多种机制来保障Java程序的执行安全。

正因为有了JVM,Java程序员编写的代码可以在任何安装了JVM的硬件和操作系统上运行,无需重新编译,实现了高度的可移植性和跨平台能力。

GC算法(垃圾回收算法)

Java虚拟机中的垃圾回收算法主要有以下几种:

  1. 标记-清除(Mark and Sweep)

    • 这是最基础的垃圾回收算法。首先遍历所有可达的对象并做上标记,然后再次遍历堆内存,未被标记的对象被视为垃圾并回收其空间。
    • 缺点是会产生内存碎片,导致后续分配大对象时可能无法找到足够的连续空间。
  2. 复制(Copying)

    • 将内存分为两个相等的区域,每次只使用其中一个区域。当这一区域满时,将存活对象复制到另一个区域,然后清空之前使用的区域。
    • 这种方法简单高效,能解决内存碎片问题,但代价是内存使用率只有50%。
  3. 标记-整理(Mark and Compact)

    • 结合了标记-清除和复制的优点,首先标记出所有活动对象,然后将存活的对象向一端移动,最后清理掉边界外的内存空间。
    • 这个过程既解决了碎片问题,又不需要两倍的内存空间。
  4. 分代收集(Generational Collection)

    • 基于“大多数对象都是朝生夕灭”的假设,将堆内存分为新生代和老年代。
    • 新生代通常使用复制算法,因为它频繁GC但每次回收的大多是短命对象。
    • 老年代则常用标记-清除或标记-整理算法,因为这里的对象生命周期长,回收频率低。

垃圾回收器

JVM提供了多种垃圾回收器,它们实现了上述算法的不同组合,以适应不同场景的需求。以下是一些常见的垃圾回收器:

  1. Serial GC:适用于单CPU环境,新生代和老年代都采用串行回收的方式,简单高效但无法充分利用多核处理器的优势。

  2. Parallel GC(也称为Throughput Collector):在多CPU环境中并行进行垃圾回收,提高吞吐量,但可能会引起较长的暂停时间。

  3. Concurrent Mark and Sweep (CMS) GC:老年代垃圾回收器,目标是减少停顿时间,大部分工作与用户线程并发执行,但在极端情况下可能会出现“ Concurrent Mode Failure”。

  4. G1(Garbage First)GC:设计用于大型堆的服务器应用,它将堆内存划分为多个大小相等的区域(Region),并采用分代收集策略,同时努力达到低延迟和高吞吐量的目标。

类加载机制

Java的类加载机制主要包括以下步骤:

  1. 加载(Loading):查找并加载类的二进制数据,通常是.class文件。

  2. 验证(Verification):检查加载的类是否有正确的内部结构,并符合Java语言规范。

  3. 准备(Preparation):为类的静态变量分配内存,并将其初始化为默认值。

  4. 解析(Resolution):将常量池中的符号引用转换为直接引用的过程,如果需要的话。

  5. 初始化(Initialization):执行类的静态初始化代码,给静态变量赋予程序员设定的初始值。

类加载器分为四种主要类型:Bootstrap ClassLoader、Extension ClassLoader、Application ClassLoader 和 Custom ClassLoader(用户自定义)。它们形成了一个层次结构,负责加载不同来源的类,且遵循双亲委派模型原则,即类加载请求先委托给父加载器处理,如果父加载器不能处理再由自己尝试加载。

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

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

相关文章

ubuntu安装idea

下载这个文件解压,我的解压在了用户目录下的X86下,接下来直接运行bin下的sh就能启动,想要在固定栏用以下步骤 创建桌面条目文件: 在 ~/.local/share/applications 目录中创建一个新的 .desktop 文件。例如,可以命名…

大数据开发流程解析

大数据开发是一个复杂且系统的过程,涉及需求分析、数据探查、指标管理、模型设计、ETL开发、数据验证、任务调度以及上线管理等多个阶段。本文将详细介绍每个阶段的内容,并提供相关示例和代码示例,帮助理解和实施大数据开发流程。 本文中的示…

C语言---------深入理解指针

目录 一、字符指针 二、指针数组: 三、数组指针: 1、定义: 2、&数组名和数组名区别: 3、数组指针的使用: 四、数组参数,指针参数: 1、一维数组传参: 2、二维数组传参&am…

华媒舍:明星祝福视频,为你送上最真挚的祝福!

引言:嗨,亲爱哒书友!在这样一个科谱详细介绍文中,我们将带你领略一份尤其的独家合辑——十部明星祝愿视频。这种视频汇聚了诸多明星为你送上的最真挚的祝福。让我们一起来探寻这种电影中蕴含的情绪和价值吧! 1.共享温暖…

数据网格和视图入门

WinForms数据网格(GridControl类)是一个数据感知控件,可以以各种格式(视图)显示数据。本主题包含以下部分,这些部分将指导您如何使用网格控件及其视图和列(字段)。 Grid Control’s…

使用langchain接入通义千问与知识图谱

文章目录 前言大前提准备工作0. 找一个key1. 手动部署2. Docker部署 该怎么开始用户的提问问答历史读取api-key使用Streamlit构建页面框架Prompt知识库的植入Prompt知识库的执行Prompt知识库详细内容植入更新布局补全页面细节 前言 这一篇文章将尝试做一个缝合怪,把…

mybatis-plus使用拦截器实现sql完整打印

shigen坚持更新文章的博客写手,擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长,分享认知,留住感动。 个人IP:shigen 在使用mybatis-plus(mybatis)的时候,往往需要…

深入理解指针(四)

目录 1. 回调函数是什么? ​2. qsort使用举例 2.1冒泡排序 2.2使用qsort函数排序整型数据 ​2.3 使用qsort排序结构数据(名字) 2.4 使用qsort排序结构数据(年龄) 3. qsort函数的模拟实现 1. 回调函数是什么? 回调函数就是⼀个通过函数指针调⽤的函数。 如果你把函数…

C# WPF入门学习主线篇(十五)—— DockPanel布局容器

C# WPF入门学习主线篇(十五)—— DockPanel布局容器 欢迎来到C# WPF入门学习系列的第十五篇。在前几篇文章中,我们探讨了 Canvas、StackPanel 和 WrapPanel 布局容器及其使用方法。本篇博客将介绍另一种强大且常用的布局容器——DockPanel。…

JavaFX 图像视图

JavaFX ImageView 控件可以在 JavaFX GUI 中显示图像。ImageView 控件必须添加到场景图中才能可见。JavaFX ImageView 控件由类表示 javafx.scene.image.ImageView。 创建一个 ImageView 通过创建类的实例来创建 ImageView 控件实例ImageView。类的构造函数ImageView需要一个…

逢3必过报数游戏-第13届蓝桥杯省赛Python真题精选

[导读]:超平老师的Scratch蓝桥杯真题解读系列在推出之后,受到了广大老师和家长的好评,非常感谢各位的认可和厚爱。作为回馈,超平老师计划推出《Python蓝桥杯真题解析100讲》,这是解读系列的第84讲。 逢3必过报数游戏&…

【文档智能 RAG】RAG增强之路-智能文档解析关键技术难点及PDF解析工具PDFlux

前言 在私域知识问答和企业知识工程领域,结合Retrieval-Augmented Generation(RAG)模型和大型语言模型(LLM)已成为主流方法。然而,企业中存在着大量的PDF文件,PDF解析的低准确性显著影响了基于…

ubuntu中安装docker并换源

使用 Ubuntu 的仓库安装 Docker sudo apt update现在,你可以使用以下命令在 Ubuntu 中安装 Docker 以及 Docker Compose: sudo apt install docker.io docker-composeDocker 包被命名为 docker.io,因为在 Docker 出现之前就已经存在一个名为…

细说MCU修改回调函数调用模式的方法

目录 1、硬件及工程 2、实现方法 (1)修改while(1)中的代码: (2)修改2 (3)修改3 (4)修改4 (5)修改5 3、下载并运行 在本文作者的文章中&a…

Web端在线/离线Stomp服务测试与WebSocket服务测试

Stomp服务测试 支持连接、发送、订阅、接收,可设置请求头、自动重连 低配置云服务器,首次加载速度较慢,请耐心等候 预览页面:http://www.daelui.com/#/tigerlair/saas/preview/lxbho9lkzvgc 演练页面:http://www.da…

飞腾银河麒麟V10安装Todesk

下载安装包 下载地址 https://www.todesk.com/linux.html 安装 yum makecache yum install libappindicator-gtk3-devel.aarch64 rpm -ivh 下载的安装包文件后台启动 service todeskd start修改配置 编辑 /opt/todesk/config/config.ini 移除自动更新临时密码 passupda…

奇思妙想-可以通过图片闻见味道的设计

奇思妙想-可以通过图片闻见味道的设计 偷闲半日享清闲,炭火烧烤乐无边。肉串飘香引客至,笑语欢声绕云间。人生难得几回醉,且把烦恼抛九天。今宵共饮开怀酒,改日再战新篇章。周四的傍晚,难得的闲暇时光让我与几位挚友相…

js 前端 Function.prototype.call.call(0[‘toString‘], *, 16)

这个函数将 数组转任意进制 Function.prototype.call.call(0[toString], *, 16)

【探索Linux】P.34(HTTPS协议)

阅读导航 引言一、HTTPS是什么1. 什么是"加密"2. 为什么要加密3. 常见的加密方式(1)对称加密(2)非对称加密 二、证书认证1. CA认证 三、HTTPS的加密底层原理✅非对称加密对称加密证书认证 温馨提示 引言 在上一篇文章中…

Vue主要使用-03

组件通讯 组件通讯也是我们需要了解的,在我们的实际开发中,我们使用的非常多,比如父组件内的数据传入到子组件,子组件的数据传入到父组件,什么是父组件什么是子组件?父组件内包含着我们的子组件,我们的父组件可以有多个子组件,父组件就是我们使用子组件拼接的。 …