【JVM】一篇文章彻底理解JVM的组成,各组件的底层实现逻辑

在这里插入图片描述

文章目录

    • JVM 的主要组成部分
    • 类加载器(Class Loader)
      • 1. 加载(Loading)
      • 2. 链接(Linking)
      • 3. 初始化(Initialization)
    • Execution Engine(执行引擎)
      • 1. 解释器(Interpreter)
      • 2. 即时编译器(JIT Compiler)
      • 3. 垃圾回收器(Garbage Collector)
    • Runtime Data Area(运行时数据区)
      • 1. 方法区(Method Area)
      • 2. 堆(Heap)
      • 3. Java 虚拟机栈(Java Virtual Machine Stack)
      • 4. 程序计数器(Program Counter Register)
      • 5. 本地方法栈(Native Method Stack)
    • Native Interface(本地接口)
    • java文件在jvm中的处理过程
    • 堆中实现深拷贝与浅拷贝原理
    • 对象分配内存底层原理

更多相关内容可查看

JVM 的主要组成部分

JVM包含两个子系统和两个组件,两个子系统为Class loader(类装载)、Execution engine(执行引擎);两个组件为Runtime data area(运行时数据区)、Native Interface(本地接口)。

对于某些面试官可能就会问到这些组成的原理,下文将详细介绍这四部分

类加载器(Class Loader)

在这里插入图片描述

  • 功能:负责加载 Java 类文件到 JVM 中。它的主要任务是找到、加载、链接和初始化类。
  • 过程
    • 加载:从文件系统、网络或其他源中读取文件。
    • 链接
      • 验证:确保字节码符合 JVM 的规范。
      • 准备:分配内存并设置类变量的初始值。
      • 解析:将符号引用转换为直接引用。
    • 初始化:执行静态初始化块和静态变量赋值。

1. 加载(Loading)

  • 定义:类加载的第一步是查找并加载 .class 文件。
  • 来源:加载可以来自多个地方,包括:
    • 文件系统(本地磁盘)
    • 网络(远程服务器)
    • 其他类加载器(如应用程序特定的加载器)
  • 实现:类加载器会读取字节流并将其转换为数据结构,以供后续处理。

对于java来说,类加载器将.class文件(字节码二进制的数据),装载到jvm中,并将类的结构封装成一个class对象放到堆中

2. 链接(Linking)

a. 验证(Verification)

  • 目的:确保加载的字节码符合 JVM 的规范,防止潜在的安全问题和错误。
  • 检查内容
    • 字节码的合法性,确保它符合 Java 虚拟机规范。
    • 确保类的结构正确,如方法的参数和返回类型等。

b. 准备(Preparation)

  • 目的:为类变量(静态变量)分配内存并设置初始值。
  • 内存分配:为类的静态字段分配内存,这些字段在方法区的运行时数据区中存储。
  • 默认值:所有类变量在准备阶段会被初始化为默认值(如 0nullfalse)。

c. 解析(Resolution)

  • 目的:将符号引用转换为直接引用。
  • 符号引用:在字节码中,类、方法和字段等通过符号名引用。
  • 直接引用:解析后,符号引用被转换为内存地址(直接引用),使得 JVM 在运行时可以直接访问。

3. 初始化(Initialization)

  • 定义:在链接完成后,类的初始化阶段会执行。
  • 执行过程
    • 执行类的静态初始化块。
    • 为静态变量赋值,通常是在定义时指定的值。
  • 静态初始化块:如果类中有静态代码块,这些代码会在初始化阶段执行,允许开发者在类加载时执行特定逻辑。

Execution Engine(执行引擎)

在这里插入图片描述

功能:负责执行字节码,将其转换为底层机器指令并由 CPU 执行。(jvm其实是无法识别字节码的,所以要转一下)

主要组成

  • 解释器(Interpreter):逐行解释字节码并执行,适合快速启动,但效率较低。
  • 即时编译器(JIT Compiler):将热点代码(经常执行的代码)编译为机器码,提高执行效率。编译后的代码存储在内存中,以供后续直接调用。
  • 垃圾回收器(Garbage Collector):负责自动管理内存,回收不再使用的对象,防止内存泄漏。

1. 解释器(Interpreter)

  • 功能:解释器逐行读取和执行Java字节码,而不是将整个字节码文件编译为机器码。这使得程序可以快速启动和运行。
  • 工作原理
    • 逐行解析:解释器将字节码指令逐行读取,解析每条指令并立即执行。对于每个字节码操作,都会进行相应的操作(如计算、对象创建等)。
    • 适用场景:适合小型应用或在开发阶段,快速迭代和测试代码时。
  • 优缺点
    • 优点:启动快,不需要预编译,适合动态变化的代码。
    • 缺点:整体执行效率低,因为每次运行都需要解析和执行字节码,增加了开销。

2. 即时编译器(JIT Compiler)

  • 功能:JIT编译器在运行时将热点代码(频繁执行的代码段)编译为机器码,从而提高执行效率。
  • 工作原理
    • 热点识别:JIT编译器监测哪些代码被频繁调用,并将这些代码编译为本地机器码。
    • 优化执行:编译后的机器码会存储在内存中,后续执行时直接调用,避免再次解析字节码。
    • 实时优化:JIT编译器还可以进行多种优化(如内联、死代码消除等),进一步提高性能。
  • 优缺点
    • 优点:执行速度快,减少了解释执行的开销,适合长时间运行的应用。
    • 缺点:初次执行时可能存在延迟,因为需要时间编译热点代码;占用更多内存以存储编译后的代码。

3. 垃圾回收器(Garbage Collector)

想深入了解这部分可查看一篇聊透内存泄露,栈内存溢出,JVM中的垃圾回收器超详细

  • 功能:自动管理内存,回收不再使用的对象,防止内存泄漏和优化内存使用。
  • 工作原理
    • 对象标记:垃圾回收器会跟踪所有对象的引用情况,标记那些不再被引用的对象。
    • 清理:标记完成后,垃圾回收器会清理这些不再使用的对象,释放内存。
    • 回收算法:常见的回收算法包括标记-清除、复制、标记-压缩等,不同的算法有不同的效率和内存使用特点。
  • 优缺点
    • 优点:自动管理内存,开发者无需手动释放内存,降低了内存管理的复杂性。
    • 缺点:可能导致性能波动,尤其是在垃圾回收时,可能会暂停应用程序的执行(称为“停顿”现象)。

Runtime Data Area(运行时数据区)

在这里插入图片描述

  • 功能:JVM 在运行 Java 程序时使用的内存区域,主要包括:
    • 方法区:存储类信息、常量、静态变量等。
    • :用于存储对象实例和数组,是所有线程共享的。
    • Java 虚拟机栈:每个线程都有独立的栈,用于存储局部变量、方法调用等。
    • 程序计数器:指示当前执行的字节码指令位置。
    • 本地方法栈:用于处理本地方法调用。

1. 方法区(Method Area)

  • 功能:方法区是JVM的一部分,用于存储类信息、常量、静态变量、即时编译后的代码等。它是所有线程共享的内存区域。
  • 内容
    • 类信息:存储类的结构,如类的名称、访问修饰符、父类信息、接口信息等。
    • 常量池:存储编译时生成的常量,包括字符串字面量和其他基本数据类型的常量。
    • 静态变量:存储用static修饰的变量,这些变量在整个类的生命周期内是共享的。
  • GC(垃圾回收):方法区也会进行垃圾回收,特别是对于常量池中的无用常量。

2. 堆(Heap)

  • 功能:堆是JVM中最大的内存区域,用于存储对象实例和数组。它是所有线程共享的,适合动态分配内存。
  • 内容
    • 对象实例:通过new关键字创建的对象。
    • 数组:存储所有类型的数组(如整型数组、对象数组等)。
  • GC(垃圾回收):堆中的内存管理是JVM的主要任务之一,使用不同的垃圾回收算法(如标记-清除、复制等)来回收不再使用的对象。

3. Java 虚拟机栈(Java Virtual Machine Stack)

  • 功能:每个线程都有独立的虚拟机栈,存储局部变量、方法调用和返回值。栈的生命周期与线程相同。
  • 内容
    • 局部变量:存储方法参数和局部变量,包括基本数据类型和对象引用。
    • 方法调用:每次方法调用时会创建一个栈帧,保存当前方法的信息和状态。
  • 栈溢出:如果栈深度超过JVM允许的最大值,将会抛出StackOverflowError

4. 程序计数器(Program Counter Register)

  • 功能:每个线程都有一个独立的程序计数器,用于指示当前执行的字节码指令的位置。它是线程私有的。
  • 内容
    • 指令地址:记录当前线程正在执行的字节码指令地址。
    • 线程切换:当线程切换时,程序计数器的值可以帮助恢复执行状态。

5. 本地方法栈(Native Method Stack)

  • 功能:本地方法栈用于处理 Java 程序中调用的本地方法(使用 JNI,即 Java Native Interface)。
  • 内容
    • 本地方法调用:存储本地方法的参数和局部变量。
    • JNI支持:可以直接调用 C/C++ 等语言编写的底层方法。
  • 栈溢出:类似于虚拟机栈,如果本地方法栈的深度超过JVM设定的最大值,将会抛出StackOverflowError

Native Interface(本地接口)

  • 功能:允许 Java 代码调用其他语言(如 C/C++)编写的本地方法。这使得 Java 可以利用系统级资源或优化性能。
  • 相关工具
    • Java Native Interface (JNI):是 Java 提供的标准机制,允许 Java 代码和本地应用程序相互调用。

java文件在jvm中的处理过程

1.java文件,通过java源码编译器,Java 源代码经过编译后生成 `.class文件,存储的是字节码(二进制数据)。

2.类加载器将字节码数据读到方法区中,并在堆中创建一个java.lang.Class对象,用来封装类在方法区内的数据结构

3.而字节码文件只是 JVM 的一套指令集规范,并不能直接交给底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由 CPU 去执行

4.在某些情况下,执行过程中需要调用本地库(Native Interface)来实现特定功能。

整体流程总结:
.java文件通过java源码编译器进行编译为.class文件,存储的是字节码(二进制数据),类加载器,通过验证、准备、解析、初始化流程、将字节码数据读到方法区中,并在堆中创建一个java.lang.Class对象,用来封装类在方法区内的数据结构,通过编译器,将字节码翻译成底层系统指令,再交由 CPU 去执行

堆中实现深拷贝与浅拷贝原理

浅拷贝只复制对象的基本属性,而引用属性仍指向原对象。

class Address {String city;public Address(String city) {this.city = city;}
}class Person implements Cloneable {String name;Address address;public Person(String name, Address address) {this.name = name;this.address = address;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}public class ShallowCopyExample {public static void main(String[] args) throws CloneNotSupportedException {Address addr = new Address("New York");Person p1 = new Person("Alice", addr);Person p2 = (Person) p1.clone();System.out.println(p1.address.city); // 输出: New Yorkp2.address.city = "Los Angeles";System.out.println(p1.address.city); // 输出: Los Angeles (引用共享)}
}

深拷贝复制对象及其引用的所有属性,确保没有共享引用。

class Address {String city;public Address(String city) {this.city = city;}public Address deepCopy() {return new Address(this.city);}
}class Person implements Cloneable {String name;Address address;public Person(String name, Address address) {this.name = name;this.address = address;}@Overrideprotected Object clone() throws CloneNotSupportedException {Person cloned = (Person) super.clone();cloned.address = this.address.deepCopy(); // 深拷贝return cloned;}
}public class DeepCopyExample {public static void main(String[] args) throws CloneNotSupportedException {Address addr = new Address("New York");Person p1 = new Person("Alice", addr);Person p2 = (Person) p1.clone();System.out.println(p1.address.city); // 输出: New Yorkp2.address.city = "Los Angeles";System.out.println(p1.address.city); // 输出: New York (无引用共享)}
}
浅拷贝就是平常对于一个实例的属性赋值,重新set就会覆盖原来的 深拷贝不会覆盖原来的属性值,而是会生成一个新的实例

对象分配内存底层原理

给对象分配内存有两种方式:

  • 指针碰撞:意思就是如果Java堆的内存是规整,即所有用过的内存放在一边,而空闲的的放在另一边。分配内存时将位于中间的指针指示器向空闲的内存移动一段与对象大小相等的距离,这样便完成分配内存工作
  • 空闲列表:如果Java堆的内存不是规整的,则需要由虚拟机维护一个列表来记录那些内存是可用的,这样在分配的时候可以从列表中查询到足够大的内存分配给对象,并在分配后更新列表记录。

在这里插入图片描述

可能存在的并发安全问题:可能出现正在给对象 A 分配内存,指针还没来得及修改,对象 B 又同时使用了原来的指针来分配内存的情况

在这里插入图片描述

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

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

相关文章

QT开发:详解 Qt 多线程编程核心类 QThread:基本概念与使用方法

1. 引言 在现代应用程序开发中,多线程编程是一个关键技术,能够显著提高程序的效率和响应速度。Qt 是一个跨平台的 C 框架,其中 QThread 类是实现多线程编程的核心类。本文将深入详解 QThread 的基本概念、使用方法及其在实际应用中的重要性。…

对于 Vue CLI 项目如何引入Echarts以及动态获取数据

🚀个人主页:一颗小谷粒 🚀所属专栏:Web前端开发 很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~ 目录 1、数据画卷—Echarts介绍 1.1 什么是Echarts? 1.2 Echarts官网地址 2、Vue CLI 项目…

第十三周:机器学习笔记

第十三周周报 摘要Abstract一、机器学习——Transformer(上)1. Sequence to Sequence(Seq 2 Seq,序列到序列模型) 的应用2. Transformer的结构2.1 Transformer encoder(Transformer 编码器) 二、Pytorch学习1. 网络模型…

将图片资源保存到服务器的盘符中

服务类 系统盘符:file-path.disk(可能会变,配置配置文件dev中)文件根路径:file-path.root-path(可能会变,配置配置文件dev中)http协议的Nginx的映射前缀:PrefixConstant.…

go解决引入私有包报错“Repository owner does not exist“的两种方式

当你写好引入的私有包,执行go mod tidy报错: Gogs: Repository owner does not exist fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists. 目前我的两种解决方案: 一、拉群整个…

freeRDP OPenssl

libusb需要下载 我使用的是VS2019编译 所以需要include 与vs2019 在cmake里面修改路径 C:/Users/JPM/source/repos/freeRDP/FreeRDP-stable-2.0/libusb-1.0.24/include/libusb-1.0 C:/Users/JPM/source/repos/freeRDP/FreeRDP-stable-2.0/libusb-1.0.24/VS2019/MS64/static/l…

模形式与态、势、感、知

模形式是数学中一个重要的研究领域,主要出现在数论、代数几何和表示论等多个学科中。模形式可以视为在某种意义上具有“对称性”的函数,这些函数在特定的条件下满足一定的变换性质。具体来说,模形式是定义在上半平面上的复值函数,…

第九节 Opencv自带颜色表操作

知识点:Look Up lTable(LUT)查找表 了解LUT查找表的作用与用法,代码实现与API介绍 -applyColorMap(src,dst,COLORMAP) -src表示输入图像 -dst表示输出图像 匹配到的颜色LUT,Opencv支持13种…

TDOA方法求二维坐标的MATLAB代码演示与讲解

引言 时间差定位(Time Difference of Arrival, TDOA)是一种用于确定信号源位置的技术,广泛应用于无线通信、声学定位等领域。通过测量信号到达多个接收器的时间差,可以计算出信号源的二维坐标。本文将通过MATLAB代码演示如何使用TDOA方法来求解二维坐标。 TDOA原理 TDOA…

第50篇 汇编语言实现中断<六>

Q:怎样设计汇编语言程序使用定时器中断实现实时时钟? A:此前我们曾使用轮询定时器I/O的方式实现实时时钟,而在本实验中将采用定时器中断的方式。新增的interval_timer.s间隔定时器的中断服务程序中增加了TIME变量,还更…

JavaScript的条件语句

if条件语句 if结构先判断一个表达式的布尔值,然后根据布尔值的真伪,执行不同的语句。所谓布尔值,指的是JavaScript 的两个特殊值,true表示真,false表示伪。 if语句语法规范 if(布尔值){语句;}var m3if(m3){console.l…

STM32嵌入式编程学习到提高:【4】UART串口打印

------------------------------------------------------------------------------------------------------------------------- 工程文件:放在百度云盘里,需要的自行下载!!! 链接: https://pan.baidu.com/s/14gRne…

Flowable7.0.1框架严重bug,流程跳转到指定节点导致流程中断

一、Bug描述 使用7.0.1版本的 moveActivityIdsToSingleActivityId 或 moveExecutionsToSingleActivityId实现节点跳转,程序不会报错,但是act_ru_task 没有生成新的任务,导致流程中断,这是相当严重的bug。 经过多次测试&#xff…

【学习笔记】TLS/SSL握手之Records

TLS / SSL会话是由记录(Records)所组成,有4种records HandshakeAlertChange Cipher SpecApplication DataHandshake和Alert Records被分为子类型(Subtypes): Handshake:Client HelloHandshake&a…

使用 Llama-index 实现的 Agentic RAG-Router Query Engine

前言 你是否也厌倦了我在博文中经常提到的老式 RAG(Retrieval Augmented Generation | 检索增强生成) 系统?反正我是对此感到厌倦了。但我们可以做一些有趣的事情,让它更上一层楼。接下来就跟我一起将 agents 概念引入传统的 RAG 工作流,重新…

Apache Iceberg 数据类型参考表

Apache Iceberg 概述-链接 Apache Iceberg 数据类型参考表 数据类型描述实例方法注意事项BOOLEAN布尔类型,表示真或假true, false用于条件判断,例如 WHERE is_active true。确保逻辑条件的正确性。INTEGER32位有符号整数42, -7可用于计算、聚合&#xf…

基于 Redis 实现滑动窗口的限流

⏳ 限流场景:突发流量,恶意流量,业务本身需要 基于 Redis 实现滑动窗口的限流是一种常见且高效的做法。Redis 是一种内存数据库,具有高性能和支持原子操作的特点,非常适合用来实现限流功能。下面是一个使用 Redis 实现…

Ubuntu环境下字体安装

本文介绍Ubuntu环境下字体安装。 软件(如Qt应用软件)开发过程中经常会涉及到字体的选择,有时候Ubuntu环境下并没有我们想要的字体,本文介绍常用字体及在Ubuntu环境下如何安装。 1.常用开源字体 有些字体商用并不是免费的&#…

Java搭建法律AI助手,快速实现RAG应用

使用AI4J快速接入RAG应用 | 结合Pinecone实现法律AI助手RAG应用 本博文给大家介绍一下如何使用AI4J快速接入OpenAI大模型,并且结合Pinecone向量数据库实现一个刑法AI助手的RAG应用。 介绍 由于SpringAI需要使用JDK17和Spring Boot3,但是目前很多应用依…

idea中.git文件夹存在但是没有git功能列表

1.问题: 该项目中已经将.git文件夹置入了,但是idea中却没有git相关的功能列表,如图: 2.解决办法: 在【文件】-【设置】-【版本控制】-【目录映射】中添加目录映射应用就好了 (【File】 -> 【S…