【JVM】深入解析Java虚拟机

目录

1. 区分JDK,JRE 和 JVM

1.1 JVM

1.2 JRE

1.3 JDK

1.4 关系总结

2. 跨平台性

3. JVM中的内存划分

4. JVM的类加载机制

5. 双亲委派模型

6. 垃圾回收机制(GC)

6.1 识别垃圾

6.1.1 单个引用

6.1.2 多个引用

6.2 释放垃圾

6.2.1 标记—释放

6.2.2 复制算法

6.2.3 标记—整理

6.2.4 分代回收


1. 区分JDK,JRE 和 JVM

1.1 JVM

JVM是 Java 程序运行的核心,负责执行 Java 字节码(.class文件),使其能在不同操作系统上运行。

特点:

  • 可以将.class文件编译为机器码执行(跨平台能力)
  • 自动垃圾回收机制(GC),管理堆、栈、方法区等内存区域。
  • 不包含开发工具或核心类库,仅负责运行编译后的程序。

1.2 JRE

JRE 是 Java 程序运行的最小环境,包含 JVM 和运行所需的核心类库(如 java.lang、java.util、java.io)。

特点:

  • 仅支持运行已编译的 Java 程序(.jar 或 .class 文件)。
  • 不包含编译器(javac)或开发工具,无法用于开发。

1.3 JDK

JDK是Java开发的完整工具包,包含JRE和一些开发工具

特点:

  • 支持编写、编译、调试和运行 Java 程序(.java → .class → 执行)。
  • 提供开发工具如 javac(编译器,可以将 .java文件编译为 .class 文件),javadoc(文档生成),jbd(调试器)等。

1.4 关系总结

  • JDK = JRE + 开发工具
  • JRE = JVM + 核心类库
  • JVM 是执行引擎,依赖 JRE 提供的类库运行程序
  • JRE是Java程序运行的最小环境配置

2. 跨平台性

JVM类似一个翻译官,是实现跨平台性的关键

java文件先通过 javac 编译为 .class 文件,JVM又会将 .class文件转换为cpu可以识别的机器指令

因此,发布一个java程序,本质上发布 .class文件即可,JVM拿到 .class文件,就会自动进行转换

  • windows上的JVM,就会把 .class文件转换为 windows 操作系统可以识别的机器指令
  • Linux 上的JVM,就会把 .class文件转换为 Linux 操作系统可以识别的机器指令
  • 不同操作系统上面的JVM是不同的

3. JVM中的内存划分

JVM在运行的过程中,相当于一个进程,进程在运行的过程中,需要从操作系统中申请一块空间(内存资源,CPU资源等),这些内存空间,支撑了后续java程序的执行,比如定义变量需要内存空间,这里获取的内存空间,并不是直接给操作系统要,而是JVM给的

JVM从操作系统中申请一大块内存空间,给java程序使用,这一大块内存空间又会根据实际的使用用途划分出不同的空间出来,这样的好处就是便于管理和提供调用效率

JVM将申请到的空间,进行区域划分,不同的区域具有不同的作用

  • 代码中new出来的对象,都存放在堆中
  • 堆也是垃圾回收的主要区域

虚拟机栈

  • 虚拟机栈的生命周期和线程相同,线程启动时创建,线程结束时销毁
  • 负责管理Java方法的调用和执行
  • 由栈帧组成,每个方法调用对应一个栈帧
  • 栈帧中存储局部变量表、操作数 、动态链接、方法返回地址等信息。

栈帧

  • 局部变量表:存放方法参数和局部变量。
  • 操作数栈:执行方法时的工作区(先进后出)
  • 动态链接:指向运⾏时常量池的方法引用(在方法中调用另一个方法)
  • 方法返回地址:PC寄存器的地址

本地方法栈

和虚拟机栈的功能类似,只不过java虚拟机站是给JVM使用,本地方法栈是给本地方法使用。(常见的本地接口JNI调用非java代码)

程序计数器

只会占用较小的空间,用于存储下一条要执行的java指令的地址

元数据区

元数据一般指的是一些辅助性质的数据

元数据区:存储被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据的。

一个程序有哪些类,每个类中有哪些方法,每个方法包含哪些指令都会被记录在元数据区中


区分一个变量在那个内存区域中,主要根据变量的类型

  • 成员变量——存储在堆中
  • 静态成员变量——存储在元数据区中   
  • 局部变量——存储在虚拟机栈中     
  • 方法——存储在元数据区
  • 方法在运行时——存储在虚拟机栈中

4. JVM的类加载机制

类加载:java进程在运行的时候,需要把 .class 文件从硬盘,读取到内存中,并进行一系列的校验解析,最终转换成可执行代码的过程的过程

类加载的过程大体分为5个步骤,加载、验证、准备、解析、初始化五个阶段

1)加载 

  • 将硬盘上的 .class文件找到并打开,读取文件中的内容(二进制字节流)
  • 将字节流表示的静态数据结构转为方法区中的运行数据结构
  • 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区中访问这个类中各种数据的入⼝。

2)验证

保证读取到的数据是合法的,符合虚拟机的约束要求,确保这些数据被运行后不回危害虚拟机自身的安全

3)准备

给读取到的类对象(static变量),分配内存空间,这时候内存空间中,默认值都为0或null

4)解析

主要针对类中的字符串常量进行处理,主要是将符号引用转换为直接引用

  • 在硬盘存储中,并没有地址这个概念,虽然没有地址这个概念,但是存在类似地址“偏移量”的概念,也可以表示数据在文件中的具体位置
  • 当数据被加载到内存中,此时就会分配地址,这时候会从符号引用变为直接引用

5)初始化

Java虚拟机真正执行类中编写的Java程序代码,完成后续的类对象初始化(触发父类的加载,初始化静态成员,执行静态代码块等)

5. 双亲委派模型

双亲委派模型是Java类加载机制中的一种重要设计原则,它定义了类加载器如何协作加载类的规则。

在Java中,存在一个模块专门执行类加载操作,称为“类加载器”,主要负责将.class文件加载到内存中,并转换为可执行的java.lang.Class类的实例

JVM中的类加载器默认存在三个,也可自定义一个

  • 启动类加载器(Bootstrap ClassLoader):负责查找标准库的目录

  • 扩展类加载器(Extension ClassLoader):  负责查找扩展库的目录

  • 应用程序类加载器(Application/System ClassLoader):负责加载用户编写的程序代码和查找第三方库的目录

这三个类加载器,存在类似二叉树的父子关系(不是父子继承关系)


双亲委派模型的工作流程:

  1. 从应用程序类加载器作为入口,开始工作,先加载用户编写的程序代码,但是不会立即搜索自己负责的目录,会把搜索任务交给父亲(扩展类加载器)
  2. 代码进入扩展类加载器的范畴,但是也不会立即开始搜索任务,而是将自己的搜索任务交给父亲(启动类加载器)
  3. 代码进入启动类加载器的范畴,但是也不会立即开始搜索工作,而是将自己的搜索任务交给父亲
  4. 但是启动类加载器不存在父亲,那么就会真正的开始搜索工作,尝试在标准库中查找符合的.class文件,如果找到了,就进入后续的验证,解析等流程中,如何没有找到,就会回到孩子(扩展类加载器)中继续尝试加载
  5. 扩展类加载器收到父类返回的任务后,开始在扩展类中查找,如果找到了,就进入后续的验证,解析等流程中,如何没有找到,就会回到孩子(应用程序类加载器)中继续尝试加载
  6. 应用程序类加载器收到父类返回的任务,开始在第三方库中查找,如果找到了进入后续的流程中,如果没有找到,会继续到孩子(自定义类加载器)中继续进行加载,但是默认不存在孩子(自定义类加载器),这时候类加载过程就会失败,抛出ClassNotFoundException异常

双亲委派模型的优点:

  • 避免重复加载类,这样严格规定查找的范围,避免重复加载类
  • 安全性,使⽤双亲委派模型也可以保证了Java的核心API不被篡改,如果没有使用双亲委派模型,而是每个类加载器加载自己的话就会出现⼀些问题(如果多个类加载器独立加载同一个类,会出现类冲突)

上述的规则是JVM中默认类加载器,遵守的规则,这样的规则,其实也可以被打破

如果我们使用自定义类加载器,指定某个目录进行查找并加载,自定义类加载器不和系统再带的类加载器进行关联(不调用默认类加载器的引用),那么自定义加载器就是独立的,不会涉及双亲委派算法

6. 垃圾回收机制(GC)

定义一个变量会使用一块内存空间,如果这个变量长时间用不到又不及时释放掉,那么内存空间会为占据,导致后面想申请内存申请不到,还有可能导致内存泄漏问题,所以垃圾回收本质上是回收内存空间,更准确的是回收对象

在JVM中存在多个区域,有些区域不需要垃圾回收,对于程序计数器、虚拟机栈、本地方法栈这三部分区域, 其⽣命周期与相关线程有关,线程销毁,会自动销毁,这里垃圾回收的主要区域是方法区和堆

6.1 识别垃圾

判断哪些对象后续还需要继续使用,那些不需要继续使用

6.1.1 单个引用

对象的使用一定伴随着引用,如果一个对象没有任何引用指向他,那么就可以视为垃圾。

匿名对象被使用完后应该自动销毁,也应该被视为垃圾。

6.1.2 多个引用

如果存在多个引用指向同一个对象,必须确保每一个对象的引用都被销毁了,才能将这个对象视为垃圾。

1)引用计数器

原理:

给每个对象再分配一个额外的空间,空间里面存储这个对象存在几个引用,可以设置一个专门的扫描线程,去获取每个对象的引用计数情况,如果发现对象的引用为0,表示这个对象可以被释放。

问题:

  • 每个对象分配额外的空间,即使每个对象安排的计数器占很小的内存,如果程序中的对象数目很多,总消耗的空间也会很多。
  • 如果出现循环引用问题,会导致引用计数器无法工作。

2)可达性分析

可达性分析会消耗更多的时间,但是不会产生循环引用这样的问题。

如果出现:6.right = null ,则会导致7不可达,7不可达也会导致8不可达。

原理:

  • 从一些特别的变量作为起点进行遍历,沿着这些变量所持有的引用。逐层往下访问,能被访问到的对象就不是垃圾,如果访问不到就是垃圾。
  • JVM中存在扫描线程会不断的对代码中已有的变量进行遍历,尽可能地在一定时间内访问到更多的对象。

特殊的变量:

  • 虚拟机栈中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象

6.2 释放垃圾

6.2.1 标记—释放

在遍历的过程中检测到标志为垃圾的对象,直接释放掉,但是一般不采用这种方法,会出现内存空间碎片化问题。

6.2.2 复制算法

将标志为垃圾的对象不直接释放掉,而是将不是垃圾的对象复制到内存的另一半里,然后将垃圾的那一半空间整体释放掉。

特点:

  • 可以解决内存碎片化问题。
  • 但是可用的内存空间变少了。
  • 如果复制的对象比较多,会导致复制的开销会很大。

6.2.3 标记—整理

类似于顺序表。删除掉标志为垃圾的对象,将正在使用的对象进行搬运,集中放在一端。

特点:

  • 可以解决内存碎片化问题。
  • 不会浪费太多的内存空间。
  • 如果存活的对象很多,搬运的开销会很大。

6.2.4 分代回收

根据不同种类的对象,采用不同的方式

  • JVM中引入了对象年龄的概念并存在专门的线程负责周期性的扫描
  • 如果一个对象被扫描的一次并可达,那么年龄就加1(初始年龄都为0)
  • JVM会根据年龄的差异将整个堆内存分为两个部分:新生代和老年代

1)当代码中new出一个新的对象,这个对象会先存储在伊甸区,创建出的新对象大部分都活不过第一轮GC,生命周期非常短。

2)  第一轮GC扫描完成后,伊甸区中少数的对象会通过复制算法拷贝到生活区中,后续gc的扫描线程会持续进行扫描,生存区中的大部分对象也会在扫描中被标记为垃圾,少数存活的会继续使用复制算法拷贝到另一个生活区中,只要这个对象能在生活区中继续存活,就会被复制算法来回拷贝到另一半的生活区中,每经历一次GC扫描,对象的年龄都会加一

3)如果这个对象在生活区中经历了很多次GC仍然存活,JVM就会认为这个对象的生命周期会很长,然后通过复制算法从生活区拷贝到老年代。

4)老年代中的对象也会被GC扫描。但是扫描的频率会大大的降低。

5)老年代中的对象,如果被GC标记为垃圾,就会按照整理的方式释放内存。

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

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

相关文章

98-基于Python的网上厨房美食推荐系统

基于Python的网上厨房美食推荐系统 - 技术分享博客 📋 目录 项目概述技术栈系统架构核心功能实现数据库设计推荐算法数据可视化部署与优化项目特色总结与展望 🎯 项目概述 项目背景 随着生活节奏的加快,越来越多的人开始关注美食制作&…

创建MyBatis-Plus版的后端查询项目

记得编码和maven库的检测&#xff01;&#xff01;&#xff01; 1、maven库导入包<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupI…

开发板RK3568和stm32的异同:

RK3568 和 STM32 是两类不同定位的处理器 / 微控制器&#xff0c;在架构、性能、应用场景等方面差异显著&#xff0c;但也存在部分共性。以下从核心特性、异同点及典型场景进行对比&#xff1a;一、核心差异维度RK3568&#xff08;瑞芯微&#xff09;STM32&#xff08;意法半导…

C# 使用iText获取PDF的trailer数据

文章目录C# 使用iText获取PDF的trailer数据iText 核心概念C# 代码示例步骤 1: 确保已安装 iText步骤 2: C# 代码程序运行效果解读 Trailer 的输出总结C# 使用iText获取PDF的trailer数据 开发程序debug的时候&#xff0c;看到了PDF有个trailer数据&#xff0c;挺有意思&#xf…

京东流量资产基于湖仓架构的落地实践

在当今数字化商业浪潮中&#xff0c;数据无疑是企业的核心资产&#xff0c;而流量数据更是电商巨头京东业务运转的关键驱动力。它广泛应用于搜索推荐、广告投放等多个核心业务场景&#xff0c;直接影响着用户体验和商业效益。但随着业务规模的不断膨胀&#xff0c;传统架构在处…

​​​​​​​【Datawhale AI夏令营】多模态RAG财报问答挑战赛:学习笔记与上分思考

一、 初识赛题——从迷茫到清晰刚看到赛题时&#xff0c;坦白说有些不知所措。“多模态”、“RAG”、“图文混排PDF”&#xff0c;这些词汇组合在一起&#xff0c;听起来就像一个庞大而复杂的工程。但当我强迫自己静下心来&#xff0c;从“终点”&#xff08;提交格式和评审规则…

数据挖掘2.6 Perceptron Modeling 感知器建模

Perceptron Modeling 感知器建模Linear Discriminants 线性判别式Loss Function 损失函数misclassification 误分类0-1 Loss/Error function 0-1损失函数Hinge Loss Function 铰链损失函数Optimization 优化算法Linear Discriminants 线性判别式 线性判别式公式 f(x;w)w1x(1)w…

使用qemu运行与GDB调试内核

目录 一、前期准备 二、内核编译 三、QEMU与GDB 1、QEMU调试参数 2、gdb vmlinux 一、前期准备 内核镜像&#xff1a;bzimage gdb&#xff1a;x86_64 QEMU&#xff1a;qemu-system-x86_64 前置知识&#xff1a; &#xff08;1&#xff09;内核编译 &#xff08;2&#x…

欧盟 Radio Equipment Directive (RED)

欧盟 Radio Equipment Directive (RED) ——从 2014/53/EU 原文到 2025-08-01 强制生效的网络安全新规&#xff0c;一次看懂全部关键点。1. 法规身份与适用范围要素内容指令全称Directive 2014/53/EU on radio equipment取代指令1999/5/EC (R&TTE)适用产品所有“有意发射/接…

【FastExcel】解决ReadSheet在Map中获取对象不准确问题(已提交PR并合并到开源社区)

解决问题&#xff1a;源码ReadSheet在同一个Map中获取对象不准确问题 PR&#xff1a;Fixed the issue where different ReadSheet objects could not get the correct value when comparing them. 一&#xff1a;问题场景 ReadSheet在同一个Map中获取对象不准确(如Map<…

【网络安全入门基础教程】TCP/IP协议深入解析(非常详细)零基础入门到精通,收藏这一篇就够了

前言 这是小编给粉丝盆友们整理的网络安全入门到精通系列第三章计算机网络中TCP/IP协议的解析&#xff0c;喜欢的朋友们&#xff0c;记得给大白点赞支持和收藏一下&#xff0c;关注我&#xff0c;学习黑客技术。TCP/IP协议包含了一系列的协议&#xff0c;也叫TCP/IP协议族&…

Latex中公式部分输入正体的字母\mathrm{c}

Latex中公式部分输入正体的字母\mathrm{c}“\mathrm{c}”如何在Word中输入\mathrm{c}“\mathrm{c}” 在 LaTeX 中&#xff0c;“\mathrm{c}” 用于在数学模式中排版“c”这个字母为罗马体&#xff08;正体&#xff09;。“\mathrm” 是罗马字体命令&#xff0c;它告诉LaTeX以罗…

Document Picture-in-Picture API拥抱全新浮窗体验[参考:window.open]

在前端开发中&#xff0c;我们经常会遇到这样的需求&#xff1a;弹出一个浮动窗口来显示一些实时信息、工具栏或视频内容。过去我们会用 window.open()&#xff0c;后来越来越多的开发者倾向于使用 Modal。但现在&#xff0c;一个更现代的 API 出现了——Document Picture-in-P…

【指南版】网络与信息安全岗位系列(三):安全运维工程师

一、安全运维工程师到底做什么&#xff1f;—— 用校园场景帮你理解简单说&#xff0c;安全运维工程师就像 “网络世界的安保队长 系统管家”&#xff1a;既要实时监控网络和系统的 “异常动静”&#xff08;类似学校保安巡逻查隐患&#xff09;&#xff0c;又要负责日常的安全…

matlab——simulink学习(5向NXP库中添加新模块)

向NXP库中添加新的函数模块一、环境二、库添加模块1.打开文件夹2.创建文件3.添加S-Function三、浏览器添加模块一、环境 Windows10、MATLAB R2022b、安装NXP的S32K1XX系列工具包 二、库添加模块 1.打开文件夹 在文件系统中找到安装工具包的位置&#xff0c;用文件资源管理器…

使用ProxySql实现MySQL的读写分离

ProxySQL简介1、ProxySQL是一款开源的使用C编写的MySQL集群代理中间件&#xff1b;2、用于在MySQL数据库和客户端之间进行负载均衡、查询缓存、故障转移和查询分发&#xff1b;3、它可以作为中间层插入到应用程序和数据库之间&#xff1b;4、特点是高效灵活&#xff0c;使用简单…

WiFi 核心概念与实战用例全解

&#x1f4d6; 推荐阅读&#xff1a;《Yocto项目实战教程:高效定制嵌入式Linux系统》 &#x1f3a5; 更多学习视频请关注 B 站&#xff1a;嵌入式Jerry 1. WiFi基础与协议 WiFi&#xff08;Wireless Fidelity&#xff09;是基于IEEE 802.11协议族的无线局域网&#xff08;WLAN…

面向远程智能终端的超低延迟RTSP|RTMP视频SDK架构与实践指南

引言&#xff1a;遥操作时代&#xff0c;视觉链路已成“主控神经元” 从工业巡检到应急救援&#xff0c;从城市安防到边境监控&#xff0c;远程操控正成为智能终端与人机协同的重要落点。而在这些场景中&#xff0c;“视觉”不再只是用来观看的工具&#xff0c;而是贯穿感知、…

C++中的继承:从基础到复杂

目录 前言 1. 继承的基本概念 2. 继承方式与访问控制 3. 派生类与基类的对象转换 4. 继承中的作用域 5. 派生类的默认成员函数 6. 继承中的特殊关系 6.1 继承与友元 6.2 继承与静态成员 7. 复杂的菱形继承问题 8. 继承与组合的选择 9. 常见面试题 总结 前言 继承…