深入了解Java虚拟机(JVM)

news2024/11/18 11:44:11

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扫描,只不过扫描的频率较低。老年代的对象被视为垃圾时会按照标记-整理算法释放内存。

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

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

相关文章

Launch学习

参考博客&#xff1a; (1) 史上最全的launch的解析来啦&#xff0c;木有之一欧 1 ROS工作空间简介 2 元功能包 src目录下可以包含多个功能包&#xff0c;假设需要使用机器人导航模块&#xff0c;但是这个模块中包含着地图、定位、路径规划等不同的功能包&#xff0c;它们的逻…

【Python】1. 背景知识

认识 Python 计算机基础概念 什么是计算机? 很多老一辈的人, 管下面这个叫做计算机. 然鹅, 它只是 “计算器”, 和计算机是有很大区别的. 现在我们所说的计算机, 不光能进行算术运算, 还能进行逻辑判断, 数据存储, 网络通信等等功能,。 以至于可以自动的完成非常复杂的工作…

SLAM基础知识-卡尔曼滤波

前言&#xff1a; 在SLAM系统中&#xff0c;后端优化部分有两大流派。一派是基于马尔科夫性假设的滤波器方法&#xff0c;认为当前时刻的状态只与上一时刻的状态有关。另一派是非线性优化方法&#xff0c;认为当前时刻状态应该结合之前所有时刻的状态一起考虑。 卡尔曼滤波是…

java垃圾回收

垃圾回收 一个对象如果不再使用&#xff0c;需要手动释放&#xff0c;否则就会出现内存泄漏。我们称这种释放对象的过程为垃圾回收&#xff0c;而需要程序员编写代码进行回收的方式为手动回收。 内存泄漏指的是不再使用的对象在系统中未被回收&#xff0c;内存泄漏的积累可能…

LCR 124. 推理二叉树

解题思路&#xff1a; 分治 class Solution {// 一个哈希表用于存储中序遍历中每个值对应的索引&#xff0c;用于快速查找HashMap<Integer,Integer> map new HashMap<>();// 保存前序遍历的结果数组int[] preorder;// 主函数&#xff0c;传入前序和中序遍历的结果…

Vue中的计算属性和方法有什么区别?

Vue.js是一款流行的JavaScript前端框架&#xff0c;提供了丰富的功能和便捷的开发方式。在Vue中&#xff0c;计算属性和方法是常用的两种方式来处理数据和逻辑。但它们之间存在一些区别&#xff0c;本文将详细介绍Vue中计算属性和方法的区别&#xff0c;并通过示例代码加深理解…

UE4c++ ConvertActorsToStaticMesh ConvertProceduralMeshToStaticMesh

UE4c ConvertActorsToStaticMesh 创建Edior模块&#xff08;最好是放Editor模块毕竟是编辑器代码&#xff09;创建蓝图函数UBlueprintFunctionLibraryUTestFunctionLibrary.hUTestFunctionLibrary.cpp:.Build.cs 目标:为了大量生成模型&#xff0c;我们把虚幻带有的方法迁移成函…

一周学会Django5 Python Web开发-Django5详细视图DetailView

锋哥原创的Python Web开发 Django5视频教程&#xff1a; 2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~共计28条视频&#xff0c;包括&#xff1a;2024版 Django5 Python we…

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

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

python-分享篇-控制摄像头

文章目录 准备代码效果 准备 安装cv2 pip install opencv-python &#xff08;如果只用主模块&#xff0c;使用这个命令安装&#xff09; pip install opencv-contrib-python &#xff08;如果需要用主模块和contrib模块&#xff0c;使用这个命令安装&#xff09; 我的代码l里…

Vue+SpringBoot打造知识图谱构建系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 知识图谱模块2.2 知识点模块2.3 学生测评模块2.4 学生成绩模块 三、系统展示四、核心代码4.1 查询知识点4.2 新增知识点4.3 查询知识图谱4.4 查询学生成绩4.5 查询学生成绩 五、免责说明 一、摘要 1.1 项目介绍 基于J…

AJAX 学习笔记(Day3)

「写在前面」 本文为黑马程序员 AJAX 教程的学习笔记。本着自己学习、分享他人的态度&#xff0c;分享学习笔记&#xff0c;希望能对大家有所帮助。推荐先按顺序阅读往期内容&#xff1a; 1. AJAX 学习笔记&#xff08;Day1&#xff09; 目录 3 AJAX 原理 3.1 XMLHttpRequest 3…

Stable Video文本生成视频公测地址——Scaling Latent Video Diffusion Models to Large Datasets

近期&#xff0c;Stability AI发布了首个开放视频模型——"Stable Video"&#xff0c;该创新工具能够将文本和图像输入转化为生动的场景&#xff0c;将概念转换成动态影像&#xff0c;生成出电影级别的作品&#xff0c;旨在满足广泛的视频应用需求&#xff0c;包括媒…

为什么要在业务系统中引入大宽表?

在高度系统化驱动的业务中&#xff0c;查看业务报表已经是一个很常见的需求了。在分工非常明确的大型企业里&#xff0c;往往有专门的数据分析团队 BI 或者数据开发团队&#xff0c;他们能够胜任此类需求&#xff08;但也未必是轻松的&#xff0c;或者说高效的&#xff09;。 …

编码器原理图

操作 旋转编码器提供两种交互方式&#xff1a; 每次用户旋转旋钮时&#xff0c;都会在 DT 和 CLK 引脚上产生低电平信号&#xff1a; 顺时针旋转会先使CLK引脚变为低电平&#xff0c;然后DT引脚也变为低电平。 逆时针旋转会使 DT 引脚首先变为低电平&#xff0c;然后 CLK 引脚…

深度学习PyTorch 之 RNN-中文多分类

关于RNN的理论部分我们已经在前面介绍过&#xff0c;所以这里直接上代码 1、 数据部分 1.1 读取数据 # 加载数据 data_path ./data/news.csv data pd.read_csv(data_path)# 预览数据的前几行 data.head()数据是csv格式&#xff0c;只有两列&#xff0c;第一列是标签&#…

2024最新外贸建站:WordPress搭建外贸独立站零基础教程

想与外国人做生意有多种方式&#xff0c;一些朋友选择在跨境电商平台上开店如&#xff08;亚马逊&#xff09;&#xff0c;而另一些朋友则决定建立自己的外贸独立站点。本篇教程主要说的是第二种方式如何快速建立自己的外贸独立站&#xff01;通过学习这篇外贸建站教程&#xf…

【AI绘画】免费GPU Tesla A100 32G算力部署Stable Diffusion

免责声明 在阅读和实践本文提供的内容之前&#xff0c;请注意以下免责声明&#xff1a; 侵权问题: 本文提供的信息仅供学习参考&#xff0c;不用做任何商业用途&#xff0c;如造成侵权&#xff0c;请私信我&#xff0c;我会立即删除&#xff0c;作者不对读者因使用本文所述方法…

Beans模块之工厂模块BeanClassLoaderAware

博主介绍&#xff1a;✌全网粉丝5W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

DataGrip(IDEA 内置)连接 SQL Server

原文&#xff1a;https://blog.iyatt.com/?p14265 测试环境&#xff1a; IDEA 2023.1SQL Server 2022 首先打开 SQL Server 配置管理工具 启用 TCP/IP 打开 Windows 服务管理 在服务列表中找到 SQL Server&#xff08;MSSQLSERVER&#xff09;&#xff0c;右键重新启…