深入了解Java虚拟机(JVM)
Java虚拟机(JVM)是Java程序运行的核心组件,它负责解释执行Java字节码,并在各种平台上执行。JVM的设计使得Java具有跨平台性,开发人员只需编写一次代码,就可以在任何支持Java的系统上运行。我们刚开始学习Java时就下载了JDK(Java开发工具包),它提供了编译、调试和运行Java应用程序所需的工具和库。JDK包括了JRE(Java运行时环境),JRE包含了Java虚拟机(JVM)。由于不同的CPU的指令集可能不同,所以相同的代码可能不能在不同的系统上都正常运行,而JVM就是为了解决这个问题,Java会先通过javac把 .java 文件编译为 java字节码(相当于Java自己的一套CPU指令)然后再由具体系统平台上的的JVM(不同系统上的JVM可能存在差异),把上述字节码转化为对应的CPU能识别的机器指令。
1. JVM中的内存区域划分
JVM其实也是一个进程(任务管理器中看到的java进程),Java程序的执行时申请的内存就是JVM从系统这边申请到的内存,JVM会先申请一块大的内存,这块内存在给Java程序使用时,又会根据实际的用图划分出不同的区域,每个区域都有不同的作用。
-
Java堆(Java Heap): Java堆是Java虚拟机管理的最大一块内存区域,用于存放对象实例,数组,类的成员变量。Java堆是所有线程共享的内存区域,是垃圾回收的重点区域。
-
Java虚拟机栈(Java Virtual Machine Stacks): Java虚拟机栈也称为栈内存,用于存储线程的方法调用、局部变量、部分结果等。每个方法在被调用时都会创建一个栈帧,并入栈;方法执行完毕后栈帧出栈。栈帧包括局部变量表、操作数栈、动态链接、方法返回地址等。
-
本地方法栈(Native Method Stack): 本地方法栈类似于Java虚拟机栈,但是它为Native方法服务,即JVM内部使用C、C++等编写的本地方法。
-
程序计数器(Program Counter Register): 程序计数器是一块较小的内存区域,用于记录当前线程下一条执行的字节码指令地址。在多线程环境下,每个线程都有独立的程序计数器。
-
元数据区(Metaspace)/ 方法区(Method Area):元数据指的是一些辅助性质的描述性质的属性,元数据区主要用于存储类的元数据信息,例如类的结构、方法信息、字段信息等。一个程序有哪些类,每个类中有哪些方法,每个方法里要包含哪些指令,都会记录在元数据区中,即元数据区储存了Java代码编译后的Java字节码
注意:一个JVM进程中,堆和元数据区只有一个,栈和程序计数器可能有多份(每个线程都有一个自己的程序计数器和栈,即每个线程都有自己的执行流)。
public class Test{private int n;//堆区private static int m;//static修饰的变量为类属性储存在元数据区public static void main(String[] args) {Test t = new Test();//t为局部变量储存在栈区,类的实例储存在堆区}
}
2.JVM的类加载机制
2.1 类加载过程
JVM的类加载机制是指JVM在运行时将类的字节码加载到内存中并进行验证、准备、解析和初始化的过程。JVM的类加载机制主要包括以下几个步骤:
-
加载(Loading):查找并加载类的字节码文件。这个过程可以通过类加载器来完成,类加载器会根据类的全限定名在文件系统、网络或其他地方找到对应的字节码文件,并将其读入内存。
-
验证(Verification):确保被加载的类的字节码是合法、符合JVM规范的。包括文件格式验证、元数据验证、字节码验证、符号引用验证等步骤。具体验证依据,在Java虚拟机规范中有明确的格式说明

-
准备(Preparation):为类的静态变量分配内存并设置默认初始值,这些变量所使用的内存都将在方法区中进行分配。
-
解析(Resolution):将类中的符号引用转换为直接引用,这个过程可以在运行时进行也可以在编译时进行。
class Test{String s = "hello"; }上面代码编译后"hello"会储存在常量池中,s中相当于保存了“hello"的字符串常量的地址,但是代码没有运行时,s和"hello"都在字节码文件中,文件中没有地址这样的概念,所以在代码运行前s中存储的是一个类似于”偏移量"的概念记录了“hello”的相对位置,就是这里的符号引用。
-
初始化(Initialization):对类进行初始化,包括执行类构造器<clinit>()方法,静态变量赋值等操作。在初始化阶段,JVM会根据程序中对类的主动使用情况来触发初始化,例如创建类的实例、访问类的静态成员、调用类的静态方法等。
2.2 双亲委派模型
在类加载过程中,JVM采用了双亲委派模型,即由多个不同层次的类加载器组成一个层次结构,每个类加载器都有自己的责任范围,当一个类需要加载时,先由最顶层的类加载器尝试加载,如果无法加载再交由下一层的类加载器,依次类推,直到最底层的类加载器。
JVM中进行类加载是由一个专门的模块“类加载器(ClassLoader)”完成的,类加载器的作用是通过“全限定类名”(带有包名的类名,例如java.land.String,可以类比为文件路径中的绝对路径)查找 .class文件,把 .class文件的数据转化为运行时需要的类对象并加载到JVM中。
JVM中默认提供了三种类加载器,分别是:
-
启动类加载器(Bootstrap ClassLoader):负责加载Java的核心类库,如java.lang包下的类。它是JVM自身的一部分,通常由C++编写,并不继承自java.lang.ClassLoader类。
-
扩展类加载器(Extension ClassLoader):负责加载Java的扩展类库,位于jre/lib/ext目录下的类库。
-
应用程序类加载器(Application ClassLoader):也称为系统类加载器,负责加载当前项目的代码目录,以及第三方库的目录。
除了这三个默认的类加载器,开发者也可以自定义类加载器来实现特定的类加载需求,比如从网络中动态加载类、加密类加载等。自定义类加载器需要继承自java.lang.ClassLoader类,并重写其中的findClass()方法来实现类的加载逻辑。
注意:这三个类加载器之间存在父子关系,上面的为父加载器,下面的为子加载器,即1是2的父亲,2是3的父亲。
双亲委派流程:当一个类加载器收到类加载请求时,它首先将这个请求委托给它的父类加载器处理。如果父类加载器无法完成此加载请求,子加载器才会尝试自己去加载。这个过程会一直递归下去直到启动类加载器。这样做的目的是保证Java核心API的稳定性,防止用户自定义的类替换掉核心类库中的类。
1. 从Application ClassLoader作为入口
2. Application ClassLoader不会立刻搜索自己负责的目录,会把任务交给父类加载器Extension ClassLoader。
3. Extension ClassLoader也不会立刻搜索自己负责的目录,也会把任务交给父类加载器Bootstrap ClassLoader。
4.Bootstrap ClassLoader没有父类加载器,就会搜索自己负责的目录查找需要的 .class文件,如果找到了就直接进入打开文件/读文件等流程中,如果没找到,则把任务交给下一级类加载器Extension ClassLoader继续尝试寻找。
5.Extension ClassLoader 接受到任务此时就会在自己负责的目录中开始寻找,如果找到了就直接进入打开文件/读文件等流程中,如果没找到,则同样把任务交给下一级类加载器Application ClassLoader继续尝试寻找。
6.Application ClassLoader 也会在自己负责的目录中开始寻找,如果找到了就直接进入打开文件/读文件等流程中,如果没找到,也会尝试把任务交给下一级,但是默认情况下Application ClassLoader没有下一级类加载器了,于是就会类加载失败抛出ClassNotFoundException异常
上述流程就保证了类加载的顺序,防止用户自定义的类替换掉核心类库中的类。例如用户自己定义了一个java.lang.String如果这个类先被加载了,java核心库中的String类就不会被加载。
3. 垃圾回收机制(GC)
垃圾回收(GC)是自动内存管理的关键技术之一。它负责清理不再使用的对象,释放内存空间。垃圾回收,回收的是堆的内存 ,所以也可以说是回收对象。
3.1 识别垃圾
判定对象后续是否会继续使用,不会继续使用的就会被视为垃圾,如果一个对象没有任何引用指向它,那么这个对象就无法被继续使用了,也就会被视为垃圾。
3.1.1 引用计数
引用计数方法并没有在JVM中使用,但是广泛运用在其他主流语言的垃圾回收机制中,如Python,PHP。
引用计数是通过给每个对象安排一个额外的空间,记录当前有几个引用指向该对象。每有一个引用指向该对象时,就把值加一,反之则减一,当这个值为0时则视为垃圾,当负责垃圾回收的扫描线程获取到这个对象的引用计数情况时,发现为0就会释放这个对象的空间。
引用计数存在两个关键的问题:
- 问题一:要给每个对象安排计数器,就会消耗额外的空间,如果对象数量很多,总的空间浪费也就很多。
- 问题二:可能产生循环引用问题,例如两个对象互相引用,但没有一个外部的引用指向它们,此时这两个对象是无法被获取到的,但他们的引用计数又不为0,也就不会被释放。
3.1.2 可达性分析
JVM采用的就是可达性分析来识别对象是否是垃圾。
可达性分析采用的方法是 遍历所有变量,JVM会遍历所有能够被直接或者间接访问到的对象,能访问到的自然不是垃圾,遍历一圈后不能访问到的就视为垃圾。
3.2 释放垃圾
找到垃圾以后就需要把垃圾对象所占的内存空间进行释放。
3.2.1 标记-清除算法
把标记为垃圾的对象直接释放。这种释放方式会导致内存碎片问题。
如图所示,释放后,会导致出现很多大大小小的内存碎片,而内存申请都是一次申请一段连续的内存空间,这就导致了部分内存碎片可能无法使用到,也就导致了空间浪费。
3.2.2 复制算法
将内存分为两个相等的部分,每次只使用其中一半。当这一半的内存用完后,就将还在使用的对象复制到另一半,然后再清除掉已经使用过的那一半内存中的所有对象。
这种方法避免了内存碎片,但是总的可用空间变少了,同时复制对象也会消耗时间。
3.2.3 标记-整理算法
将所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。
这样也可以避免内存碎片问题,但是移动对象也要消耗时间。
3.2.4 分代回收算法
JVM中采用的是分代回收算法。给每个对象引入年龄的概念,JVM中存在专门的线程负责周期性的扫描/释放对象,如果一个对象被线程扫描了一次,并且不是垃圾,该对象的年龄就会+1(初始年龄为0)。
JVM中会根据对象年龄的差异,把整个内存分成两个大的部分,新生代(年龄较小的对象)/ 老年代(年龄较大的对象),新生代又被划分为三个区域,其中大的一部分区域为 伊甸区 ,剩下两块大小相同的区域叫做 生存区 或者 幸存区 。

新的对象都是从伊甸区中被创建的,第一轮GC扫描后,没有被清除的对象就会被通过复制算法移动到生存区(即生存区相当于未被使用的那块内存),生存区中的对象下次被GC扫描后,存活的对象又会被通过复制算法移动到另一个生存区(注意两个生存区完全是对等的),每经历一次GC,对象的年龄就会+1,如果某个对象在生存区中经过了若干轮GC任然没有被清除,JVM就会认为这个对象的生命周期很长,就会把这个对象移动到老年代,老年代的对象也会被GC扫描,只不过扫描的频率较低。老年代的对象被视为垃圾时会按照标记-整理算法释放内存。
相关文章:
深入了解Java虚拟机(JVM)
Java虚拟机(JVM)是Java程序运行的核心组件,它负责解释执行Java字节码,并在各种平台上执行。JVM的设计使得Java具有跨平台性,开发人员只需编写一次代码,就可以在任何支持Java的系统上运行。我们刚开始学习Ja…...
Image Fusion via Vision-Language Model【文献阅读】
阅读目录 文献阅读AbstractIntroduction3. Method3.1. Problem Overview3.2. Fusion via Vision-Language Model 4. Vision-Language Fusion Datasets5. Experiment5.1Infrared and Visible Image Fusion 6. Conclusion个人总结 文献阅读 原文下载:https://arxiv.or…...
探索Manticore Search:开源全文搜索引擎的强大功能
在当今信息爆炸的时代,数据的快速检索变得至关重要。无论是在电子商务网站、新闻门户还是企业内部文档,高效的搜索引擎都是确保用户满意度和工作效率的关键因素之一。而在搜索引擎领域,Manticore Search 作为一款开源的全文搜索引擎ÿ…...
AI 笔记助手,你的思路整理助手
大家好,今天给大家介绍一款非常实用的 AI 笔记助手——AI Note。这款助手就像是一个贴心的小助手,能帮助我们整理笔记,提高学习和工作效率。 🤖 AI Note 可以智能总结笔记内容,准确标记重点,让我们更快地获…...
EchoServer回显服务器简单测试
目录 工具介绍 工具使用 测试结果 工具介绍 github的一个开源项目,是一个测压工具 EZLippi/WebBench: Webbench是Radim Kolar在1997年写的一个在linux下使用的非常简单的网站压测工具。它使用fork()模拟多个客户端同时访问我们设定的URL,测试网站在压力下工作的…...
车灯修复UV胶的优缺点有哪些?
车灯修复UV胶的优点如下: 优点: 快速固化:通过紫外光照射,UV胶可以在5-15秒内迅速固化,提高了修复效率。高度透明:固化后透光率高,几乎与原始车灯材料无法区分,修复后车灯外观更加…...
探讨倒排索引Elasticsearch面试与实战:从理论到实践
在当前大数据时代,Elasticsearch(以下简称为ES)作为一种强大的搜索和分析引擎,受到了越来越多企业的青睐。因此,对于工程师来说,掌握ES的面试准备和实战经验成为了必备技能之一。本文将从ES的面试准备和实际…...
网安入门18-XSS(靶场实战)
HTML实体化编码 为了避免 XSS 攻击,会将<>编码为<与>,这些就是 HTML 实体编码。 编码前编码后不可分的空格 < (小于符号)< > (大于符号)> & (与符号)&″ (双引号)"’ (单引号)'© (版权符…...
爬虫的一些小技巧总结
一、在爬虫中,爬取的数据类型如下 1.document:返回的是一个HTML文档 2.png:无损的图片,jpg:压缩后的图片,wbep:有损压缩,比png差,比jpg好 3.avgxml图像编码字符串 4.script:脚本文件,依据一定格式编写的可执行的文…...
LeetCode---386周赛
题目列表 3046. 分割数组 3047. 求交集区域内的最大正方形面积 3048. 标记所有下标的最早秒数 I 3049. 标记所有下标的最早秒数 II 一、分割数组 这题简单的思维题,要想将数组分为两个数组,且分出的两个数组中数字不会重复,很显然一个数…...
React之数据绑定以及表单处理
一、表单元素 像<input>、<textarea>、<option>这样的表单元素不同于其他元素,因为他们可以通过用户交互发生变化。这些元素提供的界面使响应用户交互的表单数据处理更加容易 交互属性,用户对一下元素交互时通过onChange回调函数来监听…...
Siamrpn++论文中文翻译(详细!)
SiamRPN: Evolution of Siamese Visual Tracking with Very Deep Networks SiamRPN:具有非常深度网络的Siamese视觉跟踪的进化 【siamrpn论文地址】 https://arxiv.org/abs/1812.11703 摘要 基于Siamese网络的跟踪器将跟踪表示为目标模板和搜索区域之间的卷积特征…...
第一篇【传奇开心果系列】Python的自动化办公库技术点案例示例:深度解读Pandas库
传奇开心果博文系列 系列博文目录Python的自动化办公库技术点案例示例系列 博文目录前言一、主要特点和功能介绍二、Series 示例代码三、DataFrame示例代码四、数据导入/导出示例代码五、数据清洗示例代码六、数据选择和过滤示例代码七、数据合并和连接示例代码八、数据分组和聚…...
基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的停车位检测系统(Python+PySide6界面+训练代码)
摘要:开发停车位检测系统对于优化停车资源管理和提升用户体验至关重要。本篇博客详细介绍了如何利用深度学习构建一个停车位检测系统,并提供了完整的实现代码。该系统基于强大的YOLOv8算法,并结合了YOLOv7、YOLOv6、YOLOv5的性能对比…...
状态模式(State Pattern)
定义 状态模式(State Pattern)是一种行为设计模式,它允许对象在其内部状态改变时改变其行为。这意味着,当对象的状态发生变化时,它的行为也会发生变化。状态模式特别适用于行为依赖于其状态的对象,而且当这…...
js之版本号排序
版本号排序 给定一个由版本号组成的数组,按照版本号由小到大排序 假如版本号如下 : ["0.1.1", "2.3.3", "0.302.1", "4.2", "4.3.5", "4.3.4.5"];原理很简单,通过自定义sort排…...
考取ORACLE数据库OCP的必要性 Oracle数据库
OCP证书是什么? OCP,全称Oracle Certified Professional,是Oracle公司的Oracle数据库DBA(Database Administrator,数据库管理员)认证课程。这是Oracle公司针对数据库管理领域设立的一项认证课程,旨在评估和…...
WordPress通过宝塔面板的入门安装教程【保姆级】
WordPress安装教程【保姆级】【宝塔面板】 前言一:安装环境二:提前准备三:域名解析四:开始安装五:安装成功 前言 此教程适合新手,即使不懂代码,也可轻松安装wordpress 一:安装环…...
Leetcoder Day25| 回溯part05:子集+排列
491.递增子序列 给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2。 示例: 输入:[4, 7, 6, 7]输出: [[4, 6], [4, 7], [4, 6, 7], [6, 7], [7,7], [4,7,7]] 说明: 给定数组的长度不会超过15。数组中的整数范围是 [-100,100]。给定数…...
【HTML】HTML基础5(特殊字符)
目录 特殊字符的作用 常用的特殊字符 使用效果 特殊字符的作用 例如 当我在两个文字间打出空格时 <p>“银河护卫队”系列 在漫威电影宇宙中一直是异数般的存在,不仅因为影片主角是一群反英雄,<strong>与超级英雄相比显得格格不入<…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...
Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...
招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...
LOOI机器人的技术实现解析:从手势识别到边缘检测
LOOI机器人作为一款创新的AI硬件产品,通过将智能手机转变为具有情感交互能力的桌面机器人,展示了前沿AI技术与传统硬件设计的完美结合。作为AI与玩具领域的专家,我将全面解析LOOI的技术实现架构,特别是其手势识别、物体识别和环境…...
实战三:开发网页端界面完成黑白视频转为彩色视频
一、需求描述 设计一个简单的视频上色应用,用户可以通过网页界面上传黑白视频,系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观,不需要了解技术细节。 效果图 二、实现思路 总体思路: 用户通过Gradio界面上…...
HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散
前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说,在叠衣服的过程中,我会带着团队对比各种模型、方法、策略,毕竟针对各个场景始终寻找更优的解决方案,是我个人和我司「七月在线」的职责之一 且个人认为,…...
