当前位置: 首页 > news >正文

深入了解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程序使用时,又会根据实际的用图划分出不同的区域,每个区域都有不同的作用。

  1. Java堆(Java Heap): Java堆是Java虚拟机管理的最大一块内存区域,用于存放对象实例,数组,类的成员变量。Java堆是所有线程共享的内存区域,是垃圾回收的重点区域。

  2. Java虚拟机栈(Java Virtual Machine Stacks): Java虚拟机栈也称为栈内存,用于存储线程的方法调用、局部变量、部分结果等。每个方法在被调用时都会创建一个栈帧,并入栈;方法执行完毕后栈帧出栈。栈帧包括局部变量表、操作数栈、动态链接、方法返回地址等。

  3. 本地方法栈(Native Method Stack): 本地方法栈类似于Java虚拟机栈,但是它为Native方法服务,即JVM内部使用C、C++等编写的本地方法。

  4. 程序计数器(Program Counter Register): 程序计数器是一块较小的内存区域,用于记录当前线程下一条执行的字节码指令地址。在多线程环境下,每个线程都有独立的程序计数器。

  5. 元数据区(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的类加载机制主要包括以下几个步骤:

  1. 加载(Loading):查找并加载类的字节码文件。这个过程可以通过类加载器来完成,类加载器会根据类的全限定名在文件系统、网络或其他地方找到对应的字节码文件,并将其读入内存。

  2. 验证(Verification):确保被加载的类的字节码是合法、符合JVM规范的。包括文件格式验证、元数据验证、字节码验证、符号引用验证等步骤。具体验证依据,在Java虚拟机规范中有明确的格式说明

  3. 准备(Preparation):为类的静态变量分配内存并设置默认初始值,这些变量所使用的内存都将在方法区中进行分配。

  4. 解析(Resolution):将类中的符号引用转换为直接引用,这个过程可以在运行时进行也可以在编译时进行。

    class Test{String s = "hello";
    }

    上面代码编译后"hello"会储存在常量池中,s中相当于保存了“hello"的字符串常量的地址,但是代码没有运行时,s和"hello"都在字节码文件中,文件中没有地址这样的概念,所以在代码运行前s中存储的是一个类似于”偏移量"的概念记录了“hello”的相对位置,就是这里的符号引用。

  5. 初始化(Initialization):对类进行初始化,包括执行类构造器<clinit>()方法,静态变量赋值等操作。在初始化阶段,JVM会根据程序中对类的主动使用情况来触发初始化,例如创建类的实例、访问类的静态成员、调用类的静态方法等。

2.2 双亲委派模型

在类加载过程中,JVM采用了双亲委派模型,即由多个不同层次的类加载器组成一个层次结构,每个类加载器都有自己的责任范围,当一个类需要加载时,先由最顶层的类加载器尝试加载,如果无法加载再交由下一层的类加载器,依次类推,直到最底层的类加载器。

JVM中进行类加载是由一个专门的模块“类加载器(ClassLoader)”完成的,类加载器的作用是通过“全限定类名”(带有包名的类名,例如java.land.String,可以类比为文件路径中的绝对路径)查找 .class文件,把 .class文件的数据转化为运行时需要的类对象并加载到JVM中。

JVM中默认提供了三种类加载器,分别是:

  1. 启动类加载器(Bootstrap ClassLoader):负责加载Java的核心类库,如java.lang包下的类。它是JVM自身的一部分,通常由C++编写,并不继承自java.lang.ClassLoader类。

  2. 扩展类加载器(Extension ClassLoader):负责加载Java的扩展类库,位于jre/lib/ext目录下的类库。

  3. 应用程序类加载器(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就会释放这个对象的空间。

引用计数存在两个关键的问题:

  1. 问题一:要给每个对象安排计数器,就会消耗额外的空间,如果对象数量很多,总的空间浪费也就很多。
  2. 问题二:可能产生循环引用问题,例如两个对象互相引用,但没有一个外部的引用指向它们,此时这两个对象是无法被获取到的,但他们的引用计数又不为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虚拟机&#xff08;JVM&#xff09;是Java程序运行的核心组件&#xff0c;它负责解释执行Java字节码&#xff0c;并在各种平台上执行。JVM的设计使得Java具有跨平台性&#xff0c;开发人员只需编写一次代码&#xff0c;就可以在任何支持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个人总结 文献阅读 原文下载&#xff1a;https://arxiv.or…...

探索Manticore Search:开源全文搜索引擎的强大功能

在当今信息爆炸的时代&#xff0c;数据的快速检索变得至关重要。无论是在电子商务网站、新闻门户还是企业内部文档&#xff0c;高效的搜索引擎都是确保用户满意度和工作效率的关键因素之一。而在搜索引擎领域&#xff0c;Manticore Search 作为一款开源的全文搜索引擎&#xff…...

AI 笔记助手,你的思路整理助手

大家好&#xff0c;今天给大家介绍一款非常实用的 AI 笔记助手——AI Note。这款助手就像是一个贴心的小助手&#xff0c;能帮助我们整理笔记&#xff0c;提高学习和工作效率。 &#x1f916; AI Note 可以智能总结笔记内容&#xff0c;准确标记重点&#xff0c;让我们更快地获…...

EchoServer回显服务器简单测试

目录 工具介绍 工具使用 测试结果 工具介绍 github的一个开源项目,是一个测压工具 EZLippi/WebBench: Webbench是Radim Kolar在1997年写的一个在linux下使用的非常简单的网站压测工具。它使用fork()模拟多个客户端同时访问我们设定的URL&#xff0c;测试网站在压力下工作的…...

车灯修复UV胶的优缺点有哪些?

车灯修复UV胶的优点如下&#xff1a; 优点&#xff1a; 快速固化&#xff1a;通过紫外光照射&#xff0c;UV胶可以在5-15秒内迅速固化&#xff0c;提高了修复效率。高度透明&#xff1a;固化后透光率高&#xff0c;几乎与原始车灯材料无法区分&#xff0c;修复后车灯外观更加…...

探讨倒排索引Elasticsearch面试与实战:从理论到实践

在当前大数据时代&#xff0c;Elasticsearch&#xff08;以下简称为ES&#xff09;作为一种强大的搜索和分析引擎&#xff0c;受到了越来越多企业的青睐。因此&#xff0c;对于工程师来说&#xff0c;掌握ES的面试准备和实战经验成为了必备技能之一。本文将从ES的面试准备和实际…...

网安入门18-XSS(靶场实战)

HTML实体化编码 为了避免 XSS 攻击&#xff0c;会将<>编码为<与>&#xff0c;这些就是 HTML 实体编码。 编码前编码后不可分的空格 < (小于符号)< > (大于符号)> & (与符号)&amp;″ (双引号)&quot;’ (单引号)&apos;© (版权符…...

爬虫的一些小技巧总结

一、在爬虫中&#xff0c;爬取的数据类型如下 1.document:返回的是一个HTML文档 2.png:无损的图片&#xff0c;jpg:压缩后的图片,wbep:有损压缩&#xff0c;比png差&#xff0c;比jpg好 3.avgxml图像编码字符串 4.script:脚本文件&#xff0c;依据一定格式编写的可执行的文…...

LeetCode---386周赛

题目列表 3046. 分割数组 3047. 求交集区域内的最大正方形面积 3048. 标记所有下标的最早秒数 I 3049. 标记所有下标的最早秒数 II 一、分割数组 这题简单的思维题&#xff0c;要想将数组分为两个数组&#xff0c;且分出的两个数组中数字不会重复&#xff0c;很显然一个数…...

React之数据绑定以及表单处理

一、表单元素 像<input>、<textarea>、<option>这样的表单元素不同于其他元素&#xff0c;因为他们可以通过用户交互发生变化。这些元素提供的界面使响应用户交互的表单数据处理更加容易 交互属性&#xff0c;用户对一下元素交互时通过onChange回调函数来监听…...

Siamrpn++论文中文翻译(详细!)

SiamRPN: Evolution of Siamese Visual Tracking with Very Deep Networks SiamRPN&#xff1a;具有非常深度网络的Siamese视觉跟踪的进化 【siamrpn论文地址】 https://arxiv.org/abs/1812.11703 摘要 基于Siamese网络的跟踪器将跟踪表示为目标模板和搜索区域之间的卷积特征…...

第一篇【传奇开心果系列】Python的自动化办公库技术点案例示例:深度解读Pandas库

传奇开心果博文系列 系列博文目录Python的自动化办公库技术点案例示例系列 博文目录前言一、主要特点和功能介绍二、Series 示例代码三、DataFrame示例代码四、数据导入/导出示例代码五、数据清洗示例代码六、数据选择和过滤示例代码七、数据合并和连接示例代码八、数据分组和聚…...

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的停车位检测系统(Python+PySide6界面+训练代码)

摘要&#xff1a;开发停车位检测系统对于优化停车资源管理和提升用户体验至关重要。本篇博客详细介绍了如何利用深度学习构建一个停车位检测系统&#xff0c;并提供了完整的实现代码。该系统基于强大的YOLOv8算法&#xff0c;并结合了YOLOv7、YOLOv6、YOLOv5的性能对比&#xf…...

状态模式(State Pattern)

定义 状态模式&#xff08;State Pattern&#xff09;是一种行为设计模式&#xff0c;它允许对象在其内部状态改变时改变其行为。这意味着&#xff0c;当对象的状态发生变化时&#xff0c;它的行为也会发生变化。状态模式特别适用于行为依赖于其状态的对象&#xff0c;而且当这…...

js之版本号排序

版本号排序 给定一个由版本号组成的数组&#xff0c;按照版本号由小到大排序 假如版本号如下 &#xff1a; ["0.1.1", "2.3.3", "0.302.1", "4.2", "4.3.5", "4.3.4.5"];原理很简单&#xff0c;通过自定义sort排…...

考取ORACLE数据库OCP的必要性 Oracle数据库

OCP证书是什么&#xff1f; OCP&#xff0c;全称Oracle Certified Professional&#xff0c;是Oracle公司的Oracle数据库DBA&#xff08;Database Administrator&#xff0c;数据库管理员)认证课程。这是Oracle公司针对数据库管理领域设立的一项认证课程&#xff0c;旨在评估和…...

WordPress通过宝塔面板的入门安装教程【保姆级】

WordPress安装教程【保姆级】【宝塔面板】 前言一&#xff1a;安装环境二&#xff1a;提前准备三&#xff1a;域名解析四&#xff1a;开始安装五&#xff1a;安装成功 前言 此教程适合新手&#xff0c;即使不懂代码&#xff0c;也可轻松安装wordpress 一&#xff1a;安装环…...

Leetcoder Day25| 回溯part05:子集+排列

491.递增子序列 给定一个整型数组, 你的任务是找到所有该数组的递增子序列&#xff0c;递增子序列的长度至少是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>“银河护卫队”系列 在漫威电影宇宙中一直是异数般的存在&#xff0c;不仅因为影片主角是一群反英雄&#xff0c;<strong>与超级英雄相比显得格格不入<…...

SciencePlots——绘制论文中的图片

文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了&#xff1a;一行…...

YSYX学习记录(八)

C语言&#xff0c;练习0&#xff1a; 先创建一个文件夹&#xff0c;我用的是物理机&#xff1a; 安装build-essential 练习1&#xff1a; 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件&#xff0c;随机修改或删除一部分&#xff0c;之后…...

cf2117E

原题链接&#xff1a;https://codeforces.com/contest/2117/problem/E 题目背景&#xff1a; 给定两个数组a,b&#xff0c;可以执行多次以下操作&#xff1a;选择 i (1 < i < n - 1)&#xff0c;并设置 或&#xff0c;也可以在执行上述操作前执行一次删除任意 和 。求…...

ffmpeg(四):滤镜命令

FFmpeg 的滤镜命令是用于音视频处理中的强大工具&#xff0c;可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下&#xff1a; ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜&#xff1a; ffmpeg…...

【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件&#xff08;System Property Definition File&#xff09;&#xff0c;用于声明和管理 Bluetooth 模块相…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战

“&#x1f916;手搓TuyaAI语音指令 &#x1f60d;秒变表情包大师&#xff0c;让萌系Otto机器人&#x1f525;玩出智能新花样&#xff01;开整&#xff01;” &#x1f916; Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制&#xff08;TuyaAI…...

3403. 从盒子中找出字典序最大的字符串 I

3403. 从盒子中找出字典序最大的字符串 I 题目链接&#xff1a;3403. 从盒子中找出字典序最大的字符串 I 代码如下&#xff1a; class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...

回溯算法学习

一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA

浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求&#xff0c;本次涉及的主要是收费汇聚交换机的配置&#xff0c;浪潮网络设备在高速项目很少&#xff0c;通…...

GO协程(Goroutine)问题总结

在使用Go语言来编写代码时&#xff0c;遇到的一些问题总结一下 [参考文档]&#xff1a;https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现&#xff1a; 今天在看到这个教程的时候&#xff0c;在自己的电…...