JVM (simple Version)

news/2024/5/10 23:13:14/文章来源:https://blog.csdn.net/pawlaibd/article/details/131673767

简介

JVM 其实就是一个Java进程 , 从操作系统申请一大块内存区域, 供 java 代码使用 .

申请出的内存 , 进一步划分 , 给出不同的用途 .

JVM 内存区域划分 :

堆中存放就是 new 出来的对象. (成员变量)

栈 是用来维护方法之间的调用关系 (局部变量)

元数据区(或者叫方法区) 存放的是类加载之后的类对象 与  静态变量 

虚拟机栈 是给 java 代码使用的, 本地方法栈 是给 jvm 内部的本地方法(C++代码)使用的 .

可以认为虚拟机栈 中包含了很多个元素, 每一个元素表示一个方法 , 每个元素称为是一个栈帧 , 每一个栈帧会包含这个方法的 入口地址 , 方法参数是啥 , 放回地址是啥, 局部变量....等

堆和元素数据区 , 在一个 jvm 进程中 , 只有一份. 栈(本地方法栈和虚拟机栈) 和 程序计数器则是存在多份的 (每个线程都要一份).

程序计数器 用来记录当前线程执行到那个指令了.

由于 函数 调用, 也有了"后进先出" 的特点 .

数据结构的栈, 是一个通用的概念, 更广泛的概念, 此处的栈, 特指 JVM 上的一块内存空间 .

JVM 类加载机制

类加载就是 .class文件 从文件(硬盘) 被加载到内存中(元数据区)这样一个过程. 

经历这么一个过程目的是要得到 类对象 .

主要分为如下 5 步骤 :

1 . 加载 (把 .class 文件找到,打开文件,将文件内容读到内存中)

2 . 验证 (检查 .class 文件格式对不对, 在官方提供的 JVM 虚拟机规范文档上详细描述了 .class 文件的格式)

3 . 准备 (给类对象分配空间)

4 . 解析 (针对 字符串 常量进行初始化) (常量池内的符号引用替换为直接引用的过程)

              字符串常量在 .class 文件中就已经存在, 但它们只是知道彼此之间的相对位置(偏移量) , 不知道自己在内存中的实际地址 .这时候的字符串常量就是 符号引用 . 直到真正加载到内存中 , 就会把字符串常量填充到内存中的特定位置上 , 但 字符串常量之间的相对位置还是一样的 , 这时字符串有了自己真正的内存地址 .

5 . 初始化 (针对类对象进行初始化 静态成员 , 执行静态代码块.......)

类加载 采用的策略是 "懒加载" , 非必要 , 不加载

触发类加载的情况有 : 1. 创建了这个类的实例 . 2. 使用了这个类的静态方法 / 静态属性

3 . 使用子类, 会触发父类的加载 .

 双亲委派模型

双亲委派模型 描述的是 : 在 加载的时候 找 .class 文件的这个过程 .

在 JVM 中内置了 3个 类加载器 ,  目的就是为了加载类.

BootStrap ClassLoader 负责加载 Java 标准库中的类.

Extension ClassLoader 负责加载一些非标准的但是 Sun / Oracle 扩展的库的类.

Application ClassLoader 负责加载项目中自己写的类 以及 第三方库 中的类 .

首先加载一个类的时候 , 是先从Application ClassLoader 开始 , 但 Application ClassLoader 会把加载任务交给父亲 , 让父亲去进行 , 当交给 Extension ClassLoader时, 它也交给了 它的父亲 BootstrapClassLoader , 交给BootstrapClassLoader 后 , 它没有父亲 , 于是自己进行加载 , 此时 BootstrapClassLoader 就会搜索自己负责的标准库目录的相关的类 , 如果找到,就加载 , 如果没有找到, 就继续有子类加载器进行加载 . 

如果BootstrapClassLoader 没有加载 , 则ExtensionClassLoader 真正搜索扩展库相关的目录,如果找到就加载, 如果没找到, 就由子类加载器进行加载.

如果ExtensionClassLoader没有加载 , 则 Application ClassLoader 真正搜索用户项目相关的目录 , 如果找到就加载, 没找到,由子类加载器进行加载.

当Application ClassLoader 也没有加载 , 那么就会抛出异常.

垃圾回收机制 GC

垃圾指的就是 不再使用的内存 .

垃圾回收:  就是把不用的垃圾帮我们自动释放了.

GC 的好处 : 非常省心 , 让程序猿写代码简单点 , 不容易出错 .

GC 的坏处 : 需要消耗额外的系统资源 , 也有额外的性能开销 .

另外 GC还有一个关键的问题 : STW (stop the world) .

STW : 如果有时候 , 内存中的垃圾已经很多了 , 此时触发一次 GC 操作 , 开销可能非常大 , 大到把系统资源吃了大半 , 另一方法GC回收垃圾的时候可能会涉及到一些锁操作 , 导致业务代码无法正常执行 , 这样的卡顿, 极端情况下, 可能是出现几十毫秒 甚至 上百毫秒 .

但 从Java 13 开始引入了 zgc 这个垃圾回收器 , 能使 STW 控制在 1ms 以下.

GC 主要是针对堆 进行释放的 

栈 : 栈帧随着方法的调用申请, 随着方法的结束而释放 , 不需要使用到 GC

程序计数器 : 随着线程销毁,不需要用到GC.

元数据区/方法区 : 存的是类对象 , 很少会 ' 卸载 '.

GC 是以 对象 为单位进行释放的 .

GC 的工作分为两个阶段 : 

1 . 找到垃圾  (也就是判定垃圾)

2 . 再进行对象的释放 .

找到垃圾

在 Java 中 , 使用对象 , 只有通过引用来使用 !
如果一个对象 , 有引用指向它 , 则就有可能被使用到 .
 不能进行释放.                                                如果一个对象 , 没有引用指向它 , 则认为不会再被使用了 .

1) 引用计数 [ java 没有使用]

给每个对象分配一个 计数器, 每次创建一个引用指向该对象, 计数器就+1. 每次该引用被销毁了 , 计数器就 -1.

缺点 :

1) 内存空间浪费的多 (利用率低) .

如果每个对象都要分配一个 计数器 , 按照4 个字节算,如果对象多了, 占用的额外空间就会很多 .

2) 存在循环引用的问题 

但是 , 如果 a 和 b 引用 销毁 , 此时 1 号 对象和 2号 对象引用计数 都 -1, 但是结果都还是 1, 不是 0 , 因此不能释放内存 , 但是这两个对象已经没有办法被访问到了.  

2) 可达性分析 (java 的做法)

可达性分析 : 把所有这些对象被组织的结构视为是树 , 从根节点出发 , 遍历树 , 所有能被访问到的对象 , 标记为 "可达" , 不能访问到的就是不可达 .不可达的就可以作为垃圾进行回收了.

可达性分析需要进行类似于 "树遍历" 这个操作相比于引用计数来说 . 会慢一点 , 但是可达性操作并不需要一直执行 , 只需要每隔一段时间执行一次就行了.

可达性分析遍历的起点 , 称为 GCroots . (栈上的局部变量,常量池中的对象,静态成员变量) , 因此代码中有很多这样的起点 , 把每个起点都往下遍历一遍 , 就完成了一次扫描过程 .

清理垃圾

1 . 标记清除

缺点 : 内存碎片问题 , 被释放的空间是零散的 , 不是连续的 , 申请内存要求是连续空间 . 

2 . 复制算法

将内存分为两半 .

 假设 2 4 6 是垃圾, 将 1 3 5 复制到另一半, 然后将左边的空间全部清除.

每次触发复制算法 , 都是向另外一侧进行赋值 , 内存中的数据拷贝过去 .

缺点

1) 空间利用率低

2) 如果要是垃圾少 , 有效的对象多 , 复制成本就比较大了 . 

3 . 标记整理

解决了复制算法的缺点 , 类似于 顺序表删除中间元素 , 会有元素搬运的操作.

假设 要删除 1 2 4 , 得到如下 :

虽然说保证了空间利用率 , 也解决了内存碎片化问题 , 但是 效率不高 , 如果搬运的空间比较大, 此时开销也很大 . 

分代回收

上述的 3 个 方法都不完美 , 都有明显的缺点 .

因此基于上述的 3 中方法 , 搞了一个复合策略 "分代回收".

基于一个经验规律 : 如果一个东西, 存在的时间比较长了 , 那么大概率还会继续的长时间的持续存在下去 .

给对象引入一个概念 : 年龄 (这里的年龄指的是 熬过 GC 的轮次) , 年龄越大表示 存在的时间就越久 .(经过了一轮可达性分析的遍历, 发现这个对象还不是垃圾, 这就是"熬过了一轮GC")

划分为一系列区域 : 

伊甸区 : 放年纪小的对象 . 比如: 刚 new 出来的对象.

熬过一轮GC的对象就放到 幸存区 . (由伊甸区放到幸存区 使用的是复制算法) (幸存区同一时刻只能使用一个 , 在两个幸存区之间来回拷贝 , 如果这个对象已经在两个幸存区中来回拷贝很多次了, 这时候就要进入老年代了) 

老年代 : 放年纪大的对象 . (生命周期普遍更长, 因此针对老年代, 也要周期性GC 扫描, 但是频率更低了) 如果老年代的对象是垃圾了 , 使用 标记整理的方式进行释放 .

实际上 , JVM 在实现的时候, 会有一些差异 , 但会按照上述算法思想展开.有一些变化.

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

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

相关文章

计算机毕设 大数据房价数据分析及可视化 - python 房价分析

文章目录 1 课题背景2 数据爬取2.1 爬虫简介2.2 房价爬取 3 数据可视化分析3.1 ECharts3.2 相关可视化图表 4 最后 1 课题背景 房地产是促进我国经济持续增长的基础性、主导性产业。如何了解一个城市的房价的区域分布,或者不同的城市房价的区域差异。如何获取一个城…

自动驾驶与智能网联场地测试一体化装备应用

自动化驾驶层级与结构 L1:能够辅助驾驶员玩车某些驾驶任务制动防抱死系统 (ABS),车身电子稳定系统 (ESP)等,这些配置就是L1级别的运用。 L2:部分自动化,在L2的级别里,必须要具备的是自适应巡航系统,主动车道保持系统自动刹车辅助系统以及自动泊车系统等系统。 L3:有条件…

Qt + QR-Code-generator 生成二维码

0.前言 之前使用 libgrencode 生成二维码,LGPL 协议实在不方便,所以需要找一个 github 星星多的,代码简单最好 header-only,协议最好是 MIT 或者兼容协议而不是 GPL 或者 LPGL。 QR-Code-generator 正好符合这个要求&#xff0c…

Linux和Shell笔记-1相关概念理解

Unix和Linux关系 UNIX是最早的商业操作系统之一,由贝尔实验室(AT&T Bell Laboratories)于 1970 年代开发。UNIX 是一个多用户、多任务的操作系统,具有强大的命令行界面和可扩展性。 Linux 是一个开放源代码的类 UNIX 操作系统…

​LeetCode解法汇总931. 下降路径最小和

目录链接: 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目: https://github.com/September26/java-algorithms 原题链接:力扣 描述: 给你一个 n x n 的 方形 整数数组 matrix ,请你找出并返回通过 matr…

小白到运维工程师自学之路 第五十一集 (三剑客之sed)

一、概述 sed是一个流式文本编辑器,可以对文本进行搜索、替换、删除等操作。它是一个非交 互式的命令行工具,通常用于处理大量的文本数据。sed的工作方式是逐行读取输入文 本,按照预定义的命令对每一行进行处理,并输出结果。它…

使用STM32 再实现电动车防盗钥匙扣

实现目标 1. 点击遥控器 A 按键,系统进入警戒模式,一旦检测到震动(小偷偷车),则喇叭发出声响报警 2. 点击遥控器 B 按键,系统退出警戒模式,再怎么摇晃系统都不会报警 硬件介绍 1. 震动传感器…

解决uniapp运行手机基座出现的问题

常见的问题:(往往在更新编辑器版本后会出现以下问题) 问题1.明明已经连接到手机,就是检测不到设备 问题2.同步资源失败,未得到同步资源的授权 解决办法汇总 问题1解决办法: 方法一:进入HBuild…

【socket编程】TCP服务器、UDP服务器、本地套接字【C语言代码实现】

目录 0. 准备知识 0.1 大小端概念 0.2 网络字节序和主机字节序的转换 0.3 点分十进制串转换(IP地址转换函数) 0.4 IPV4结构体:(man 7 ip) 0.5 IPV6套接字结构体:(man 7 ipv6) …

实现跨语言互动:如何在Python中调用Java的JavaParser库解析Java源代码

1、背景 在多语言开发环境中,我们经常需要进行跨语言的操作。有时,我们可能会在Python环境下需要使用Java的库或者功能。这个博客将展示如何在Python中调用Java的JavaParser库来解析Java源代码。 2、需求 在许多软件开发场景中,我们可能需…

【算法与数据结构】239、LeetCode滑动窗口最大值

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析:这道题我们如果用暴力破解法需要 O ( n ∗ k ) O(n*k) O(n∗k)的复杂度。思索再三,我们需要…

【新版系统架构】第十九章-大数据架构设计理论与实践

大数据处理系统架构 大数据处理系统面临挑战 如何利用信息技术等手段处理非结构化和半结构化数据如何探索大数据复杂性、不确定性特征描述的刻画方法及大数据的系统建模数据异构性与决策异构性的关系对大数据知识发现与管理决策的影响 大数据处理系统架构特征 鲁棒性和容错…

【基于FPGA的芯片设计】RISC-V的20条指令CPU设计

实验板卡:xc7a100tlc sg324-2L,共20个开关 实验要求:

危机现场 | 如果给你25万美元,你会登上泰坦号吗?

点击文末“阅读原文”即可参与节目互动 剪辑、音频 / 小黑 运营 / SandLiu 卷圈 监制 / 姝琦 封面 / 姝琦Midjourney 产品统筹 / bobo 场地支持 / 声湃轩天津录音间 这是我们更名为记者下班后的第一期节目,临时把危机现场(原为死神来了&#xff09…

音视频编码实战-------pcm+yuv数据转成MP4

文章目录 1.编码流程图2.相关模块及函数2.1 编码器相关API2.2 复用器相关API2.3 重采样相关API注意点 简单的编码流程相关代码 1.编码流程图 2.相关模块及函数 2.1 编码器相关API avcodec_find_encoder: 根据编码器ID查找编码器 avcodec_alloc_context3:创建编码器上下文 avc…

【Arduino小车实践】PID应用之四驱小车

一、 PID公式 二、 PID应用的必要性 1. 四驱小车运动 左边两个驱动轮和右边两个驱动轮的速度相同直线右边轮子的速度大于左边轮子的速度左偏右边轮子的速度小于左边轮子的速度 右偏 2. 产生多种运动的原因 小车的4个电机,减速箱以及车轮在物理层面上存在误差&am…

【文章系列解读】Nerf

1. Nerf NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis 2020年8月3日 (0)总结 NeRF工作的过程可以分成两部分:三维重建和渲染。(1)三维重建部分本质上是一个2D到3D的建模过程&#xff…

两种传输层协议TCP和UDP【图解TCP/IP(笔记十二)】

文章目录 两种传输层协议TCP和UDPTCP与UDP区分UDP的特点及其目的TCP的特点及其目的 两种传输层协议TCP和UDP 在TCP/IP中能够实现传输层功能的、具有代表性的协议是TCP和UDP。 ■ TCP TCP是面向连接的、可靠的流协议。流就是指不间断的数据结构,你可以把它想象成排…

排序算法笔记-归并排序

归并排序 简介 通过找到中间值,然后递归分别从左区间和右区间找中间值,最终将所给的值划分为单个块,然后进行一步一步回溯,分块由两个单个分区排序后合成一个,以此类推,最后实现有序排序 时间复杂度 最…

计算机网关原理、子网掩码原理(路由器、交换机)(网关:与以太网接口关联的路由)

文章目录 网关网关的历史网关的功能网关的原理相关疑问为什么用子网掩码与IP地址进行与运算来确定一个IP地址所属的子网?网关地址是谁定的,是配置路由的人随意定的吗?(配置人员定的)如何正确设置网关地址(路…