Java:JVM

1.JVM内存区域的划分

一个Java写的程序跑起来,就得到了一个Java进程 = JVM + 上面运行的字节码指令;

进程:操作系统资源分配的基本单位;

内存区域的划分:

1.程序计数器

在内存空间里(比较小的空间),保存了下一个要执行的指令的内存地址(元数据区的地址);

这里的"下一条要执行的指令"是Java的字节码(不是CPU的二进制机器语言);

2.堆

JVM上最大的空间, new出来的对象都在堆上;

3.栈

函数中的局部变量,函数的形参,函数之间的调用关系;

分为Java虚拟机栈(JVM之上,运行的Java代码的函数调用关系)本地空间栈(JVM里头C++代码的函数调用关系);

4.元数据区(方法区)

Java程序中的指令.指令都是包含在类的方法中的;

保存了代码中涉及到的类的相关信息; 类的static属性也被包含在其中;

---------------------------------------------------------------------------------------------------------------------------------

在一个Java进程中,元数据区和堆是只有一份的.

程序计数器和栈则可能有多份;

当一个Java进程中有多个线程的时候,每个线程都有自己的程序计数器和栈,线程就代表一个"执行流";

---------------------------------------------------------------------------------------------------------------------------------

代码中的变量都处在上述的哪个区域呢?

一个变量处在哪个内存区域,和变量是不是"内置类型"无关,而是和变量的形态有关;

(1)局部变量 -> 栈;

(2)成员变量 -> 堆;

(3)静态成员变量 -> 元数据区(方法区);

---------------------------------------------------------------------------------------------------------------------------------

2.JVM类加载的过程

Java程序 -> .java文件;

javac编译 -> .class文件;

运行Java进程的时候,JVM就要读取.class里面的内容,并且执行里面的命令;

读取.class内容的过程就是类加载的过程.

把类涉及到的字节码从硬盘读取到内存中(元数据区).

加载一个.class文件,就会用.class里的指令创建一个类对象.

类对象中就包含了.class文件中的各种信息,基于类对象就能创建该类的实例;

比如:类的名字;

类有哪些属性,属性名是什么,每个属性类型是什么:public/private;

类有哪些方法,每个方法名是什么,参数是什么,方法的类型:public/private;

继承的父类有哪些,实现的接口有哪些...

因为有了类对象,才能进行反射,反射的api都是从类对象中获取信息的;

类加载的输入:.class文件(类的全限定名);

类加载得到的结果:内存中对应的类对象;        

---------------------------------------------------------------------------------------------------------------------------------

类加载的具体步骤

1.加载

把.class文件找到; 在代码中先见到类的名字,然后进一步的找到对应的.class文件.

打开并读取文件内容;

---------------------------------------------------------------------------------------------------------------------------------

2.验证

验证读到的.class文件中的数据是否正确,是否合法;

Java标准文档中,明确定义了.class文件的格式是怎么样的;

magic:计算机圈子约定俗成的做法.

二进制文件,会在开头的若干个字节,设置一个固定的常数进去;

通过这个常数,标识当前这个文件是啥样的文件.

minor_version/major_version:需要确保编译时使用的JDK和运行时使用的JDK版本一致;

JDK是可以向前兼容的(使用Java8的JDK编译出的.class文件放到Java17一般是可以运行的)

---------------------------------------------------------------------------------------------------------------------------------

3.准备

分配内存空间;

最终需要得到类对象 -> 需要内存;

根据刚才读取到的内容,确定出类对象所需要的内存空间,申请这样的内存空间,

并且把内存空间中所有的内容,都初始化为0;

(Java创建一个内存空间,都会把这个内存空间全设为0,后续再进一步初始化;

C/C++则不会进行置零操作,此时内存上对应的数据是上次使用残留的)

---------------------------------------------------------------------------------------------------------------------------------

4.解析

主要是针对类中的字符串常量进行处理;

解析阶段是 Java 虚拟机将常量池内的符号引用替换为直接引用的过程,也就是初始化常量的过程.

有很多其他的指令使用"hello"字符串常量,在文件中,这些指令就会使用"偏移量"表示"hello"字符串;

---------------------------------------------------------------------------------------------------------------------------------

5.初始化

针对类对象做最终的初始化操作;

执行静态成员的赋值语句;

执行类中的静态代码块;

针对父类也要进行加载(如果当前加载的类有父类,并且父类还没有被加载,这个环节也会触发对父类的加载);

---------------------------------------------------------------------------------------------------------------------------------

双亲委派模型

更准确应该叫单亲委派模型/父亲委派模型;

类加载五个部分,第一个步骤中里面的一个环节;

给定类的全限定名,找到对应的class文件的位置;

---------------------------------------------------------------------------------------------------------------------------------

类加载器:JVM中的功能模块,JVM中,已经内置了一些类加载器完成上述的"类加载"过程;

JVM默认有三个类加载器:

BootstrapClassLoader;(爷爷)

负责加载标准库的类;(标准库类,有一个专门的存放位置)

ExtensionClassLoader;(爸爸)

负责加载一些扩展类;(JVM厂商,希望对Java的功能做出一些扩展)

ApplicationClassLoader;(儿子)

负责加载第三方库中的类/自己写的代码的类; 

注意:

这里的父子关系不是Java中父类子类这样的继承关系;

每个类加载器有个parent这样的引用指向父亲;

---------------------------------------------------------------------------------------------------------------------------------

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

输入:类的全限定名(字符串),类似于java.lang.String

得到:找到对应的.class文件.

请求先会一直向上传递,若父亲没有找到,请求才会交给儿子;

若在某一加载器找到了,请求就结束了,就会执行类加载2345步骤;

如果都没有找到,就会抛出异常ClassNotFoundException;

这样模型的目的就是:防止用户自己写的类,把标准库的类覆盖掉.

保证标准库的类,被加载的优先级是最高的,标准库其次,第三方库优先级最低;

---------------------------------------------------------------------------------------------------------------------------------

3.JVM的垃圾回收机制(GC)

C/C++中,malloc/new一个对象,都需要手动释放内存free/delete,如果不释放就会造成内存泄露;

垃圾回收机制就是为了让程序员可以放心大胆的new对象,防止内存泄漏问题;

GC也是有代价的,需要消耗一定的性能,进行GC的时候可能会触发STW问题(Stop The World)导致程序卡顿,故C/C++没有引入GC;

GC需要负责回收的区域有哪些:

1.程序计数器/栈:

都是跟随线程的,不需要GC.

2.堆:

GC回收的主战场,

3.元数据区:

类对象->类加载,一个程序中要加载的类都是有上线的.不会出现无限增长,内存泄漏的情况;

---------------------------------------------------------------------------------------------------------------------------------

GC是如何进行回收的:

以对象为维度进行回收的.

---------------------------------------------------------------------------------------------------------------------------------

1.先找出谁是垃圾.

需要针对每个对象分别判定.

方案一:引用计数(Java没有采纳)

在Java中使用对象一般都是通过"引用"来实现的;

如果一个对象如果没有引用指向了,就可以认为这个对象是垃圾了;

给每个对象分配一个计数器,衡量有多少个引用指向,

每次增加一个引用,计数器+1; 每次减少一个引用,计数器-1;

当计数器减为0,此时这个对象就是垃圾了;

 在JVM中并没有采纳,Python/PHP使用这种方案;

存在以下两个问题:

(1)消耗额外的空间;

(2)引用计数可能会导致"循环引用"使得上述的判定出错;(需要引入"环路检测"机制来解决,代价更大了)

---------------------------------------------------------------------------------------------------------------------------------

方案二:可达性分析(Java采用的方案)

用时间换空间;

在JVM中,专门搞了一波线程,周期性的扫描代码中的所有对象,判断某个对象是否"可达"(可以被访问到)

对应的,"不可达"的对象就是垃圾了;

可达性分析的起点叫做GC root;

一个程序中不是只有一个GC root, 而是有很多个;

哪些变量可以作为GC root 呢:

(1)栈上的局部变量(引用类型);

(2)方法区中,静态的成员(引用类型);

(3)常量池中引用指向的对象;

把所有的GC root都遍历一遍 ,针对每一个尽量往下延伸;

---------------------------------------------------------------------------------------------------------------------------------

2.释放垃圾的内存空间.

方案一:标记-清除方法

针对内存中的对应对象进行释放:

这样的做法,会引入"内存碎片问题":

很可能要释放的多个内存,不是连续的,

虽然把上述内存释放掉,但是整体上这些释放掉的空间并没有连在一起;

后续申请内存空间的时候就可能申请不了,因为申请的内存是连续的;

---------------------------------------------------------------------------------------------------------------------------------

方案二:复制算法

同一时刻只使用内存的其中一半:

把不是垃圾的对象都拷贝到内存的另一侧,然后把存放垃圾的这一半内存全部释放掉;

缺点:

1.内存空间利用率低;

2.如果存活下来的对象比较多,复制成本也比较大;

方案三:标记-整理方法

---------------------------------------------------------------------------------------------------------------------------------

非常类似于顺序表删除中间元素的过程;

缺点:搬运的开销也比较大;

---------------------------------------------------------------------------------------------------------------------------------

方案四:分代算法(JVM采取的方法)

把上述几个方案综合一下,取长补短;

JVM根据对象的年龄(根据周期性的可达性分析来计算年龄),把对象进行区分:

新生代:一般创建的对象都会进入新生代;

老年代:大对象和经历了 N 次(⼀般情况默认是 15 次)垃圾回收依然存活下来的对象会从新生代

移动到老年代.

刚创建的对象就处在伊甸区(比较大);

根据经验规律,绝大部分对象是活不过第一轮GC的,

留存下来的对象,就会被拷贝到幸存区(比较小,内存浪费少);

幸存区是两个相等的空间,按照复制算法进行处理;

反复进行多次...

当对象年龄不断增长就会被放到老年代的区域;

根据经验规律,老年代的对象,生命周期比较长;

对于老年代,进行GC的频率就会降低,另外老年代是通过标记整理方法来回收(次数比较少,时间开销浪费也少);

---------------------------------------------------------------------------------------------------------------------------------

垃圾回收器

"分代回收"是JVM的GC中的基本思想方法.

具体落实到JVM实现层面上,JVM还提供了多种的"垃圾回收器";

对上述的分代回收做进一步的拓展和实现;

关注CMS/G1即可

CMS

把整个GC过程拆成多个阶段,能和业务线程并发执行的就尽量并发,

尽可能减少STW的时间;

G1

把整个内存分成很多块,不同的颜色/字母表示这是新生代(伊甸区/幸存区)/老年代;

进行GC的时候,不要求一个GC就把所有的内存都回收一边,而是一轮GC只回收其中的一部分;

限制一轮GC花的时间/工作量,使STW的时间在可控范围内;

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

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

相关文章

阿里公告:停止 EasyExcel 更新与维护

最近,阿里发布公告通知,将停止对知名 Java Excel 工具库 EasyExcel 的更新和维护。EasyExcel 由阿里巴巴开源,作者是玉箫,在 GitHub 上拥有 30k stars、7.5k forks 的高人气。 据悉,EasyExcel 作者玉箫去年已从阿里离…

安卓智能对讲终端|北斗有源终端|三防对讲机|单兵终端|单北斗

在当今快速发展的通信技术时代,智能对讲手持机已成为众多行业领域中不可或缺的通讯工具。QM240T安卓智能对讲手持机,作为一款集先进技术与实用功能于一身的高端设备,凭借其卓越的性能和多样化的应用特性,正逐步引领对讲机市场的革…

5G智能对讲终端|北斗有源终端|北斗手持机|单兵|单北斗

在当今这个快速发展的数字化时代,5G技术的广泛应用正以前所未有的速度推动着各行各业的变革。作为这一技术浪潮中的重要一环,5G智能终端QM630D凭借其卓越的性能和多样化的功能,在林业、渔业、安保、电力、交通等多个领域展现出了巨大的应用潜…

【计网】数据链路层笔记

【计网】数据链路层 数据链路层概述 数据链路层在网络体系结构中所处的地位 链路、数据链路和帧 链路(Link)是指从一个节点到相邻节点的一段物理线路(有线或无线),而中间没有任何其他的交换节点。 数据链路(Data Link)是基于链路的。当在一条链路上传送数据时&a…

重学SpringBoot3-整合 Elasticsearch 8.x (二)使用Repository

更多SpringBoot3内容请关注我的专栏:《SpringBoot3》 期待您的点赞👍收藏⭐评论✍ 整合 Elasticsearch 8.x (二)使用Repository 1. 环境准备1.1 项目依赖1.2 Elasticsearch 配置 2. 使用Repository的基本步骤2.1 创建实体类2.2 创…

计算机课程管理:Spring Boot与工程认证的协同创新

3系统分析 3.1可行性分析 通过对本基于工程教育认证的计算机课程管理平台实行的目的初步调查和分析,提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本基于工程教育认证的计算机课程管理平…

<项目代码>YOLOv8 苹果腐烂识别<目标检测>

YOLOv8是一种单阶段(one-stage)检测算法,它将目标检测问题转化为一个回归问题,能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法(如Faster R-CNN),YOLOv8具有更高的…

游戏引擎学习第四天

视频参考:https://www.bilibili.com/video/BV1aDmqYnEnc/ BitBlt 是 Windows GDI(图形设备接口)中的一个函数,用于在设备上下文(device context, DC)之间复制位图数据。BitBlt 的主要用途是将一个图像区域从一个地方复…

SPIRE: Semantic Prompt-Driven Image Restoration 论文阅读笔记

这是一篇港科大学生在google research 实习期间发在ECCV2024的语义引导生成式修复的文章,港科大陈启峰也挂了名字。从首页图看效果确实很惊艳,尤其是第三行能用文本调控修复结果牌上的字。不过看起来更倾向于生成,对原图内容并不是很复原&…

如何平滑切换Containerd数据目录

如何平滑切换Containerd数据目录 大家好,我是秋意零。 这是工作中遇到的一个问题。搭建的服务平台,在使用的过程中频繁出现镜像本地拉取不到问题(在项目群聊中老是被人出来😅)原因是由于/目录空间不足导致&#xff0…

Sharding运行模式、元数据、持久化详解

运行模式 单机模式 能够将数据源和规则等元数据信息持久化,但无法将元数据同步至多个Sharding实例,无法在集群环境中相互感知。 通过某一实例更新元数据之后,会导致其他实例由于获取不到最新的元数据而产生不一致的错误。 适用于工程师在本…

基于springboot+小程序的鲜花管理系统(鲜花1)

👉文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 本网上花店微信小程序分为管理员还有用户两个权限,管理员可以管理用户的基本信息内容,可以管理公告信息以及鲜花信息,能够与用户进行相互交流等操作&am…

金融学期末速成笔记

【拯救者】金融学速成(基础习题) 重点: 市场经济是发达的商品经济。在市场经济条件下,市场机制作为资源配置方式,发挥基础性作用。 除具有商品经济的一般特征外,与商品经济相比,市场经济还具有一些新的特征…

后悔没早点知道,Coze 插件 + Cursor 原来可以这样赚钱

最近智能体定制化赛道异常火爆。 打开闲鱼搜索"Coze 定制",密密麻麻的服务报价直接刷屏,即使表明看起来几十块的商家,一细聊,都是几百到上千不等的报价。 有趣的是,这些智能体定制化服务背后,最核心的不只是工作流设计,还有一个被很多人忽视的重要角色 —— …

嵌入式采集网关(golang版本)

为了一次编写到处运行,使用纯GO编写,排除CGO,解决在嵌入式中交叉编译难问题 硬件设备:移远EC200A-CN LTE Cat 4 无线通信模块,搭载openwrt操作系统,90M内存

基于Multisim数字电子秒表0-60S电路(含仿真和报告)

【全套资料.zip】数字电子秒表电路Multisim仿真设计数字电子技术 文章目录 功能一、Multisim仿真源文件二、原理文档报告资料下载【Multisim仿真报告讲解视频.zip】 功能 1.秒表最大计时值为60秒; 2. 2位数码管显示,分辨率为1秒; 3.具有清零…

昇思大模型平台打卡体验活动:项目2基于MindSpore通过GPT实现情感分类

昇思大模型平台打卡体验活动:项目2基于MindSpore通过GPT实现情感分类 1. 载入与处理数据集 在情感分类任务中,我们使用了IMDB数据集,首先需要对数据进行加载和处理。由于原数据集没有验证集,我们将训练集重新划分为训练集和验证…

Mac如何实现最简单的随时监测实时运行状态的方法

Mac book有着不同于Windows的设计逻辑与交互设计,使得Mac book有着非常棒的使用体验,但是在Mac电脑的使用时间过长时,电脑也会出现响应速度变慢或应用程序崩溃的情况,当发生的时候却不知道什么原因导致的,想要查询电脑…

有趣的Midjourney作品赏析(附提示词)

中文提示词:国风少年 C4D软件,高分辨率,超细节,超现实主义, 英文提示词:National Style Youth Cinema4D,high resolution,hyper detailed,surrealism, --niji 6 --ar 1:1 中文提示词:粘土模型,男性穿着中世纪欧洲蓝色盔甲&#x…

时序预测 | gamma伽马模型锂电池寿命预测 EM算法粒子滤波算法结合参数估计

时序预测 | gamma伽马模型锂电池寿命预测 EM算法粒子滤波算法结合参数估计 目录 时序预测 | gamma伽马模型锂电池寿命预测 EM算法粒子滤波算法结合参数估计预测效果基本介绍参考资料 预测效果 基本介绍 gamma伽马模型锂电池寿命预测 EM算法粒子滤波算法结合参数估计 伽马模型、…