JVM详解(包括JVM内存模型与GC垃圾回收)

news2025/4/21 15:33:36

📖前言:

        学会使用Java对于一个程序员是远远不够的。Java语法的掌握只是一部分,另一部分就是需要掌握Java内部的工作原理,从编译到运行,到底是谁在帮我们完成工作的?

        接下来着重对Java虚拟机,也就是JVM有一个深刻认识,对日后完成项目的开发或是更底层的开发有很大的帮助。

        接下来说的均为个人的一点小见解和观点,希望大家多多指点!

🎈JVM:

        JVM 是 Java Virtual Machine 的简称,意为 Java虚拟机。

虚拟机是指通过软件模拟的具有完整硬件功能的、运⾏在⼀个完全隔离的环境中的完整计算机系统。

常⻅的虚拟机:JVM、VMwave、Virtual Box。

JVM 和其他两个虚拟机的区别:
1 .  VMwave与VirtualBox是通过软件模拟物理CPU的指令集,物理系统中会有很多的寄存器;
2.JVM则是通过软件模拟Java字节码的指令集,JVM中只是主要保留了PC寄存器,其他的寄存器都进⾏了裁剪。
JVM 是⼀台被定制过的现实当中不存在的计算机。

🎨JVM与JDK的关系?

        知道JVM的基础概念,可能现在就有点懵了,JVM和JDK有什么关系吗?那么开始梳理一下与JAVA开发相关的组成部分;

✨JDK:

        官方JDK(Java Development Kit) 是Java开发工具包,包含了Java编译器(javac)、Java程序打包工具(jar)、Java程序运行环境(JRE)、文档生成工具(javadoc)以及其他开发工具,如调试的工具(jdb)。JDK是为Java开发人员提供的完整开发环境,包含了开发和运行Java程序所需的一切。

         非官方:JDK就是一个工具包,JDK是JRE的超集,JDK包含了JRE的所有开发,调试和监视应用程序等工具。当要开发Java应用程序时,需要安装JDK.

(JDK里面的工具非常多,是这么多工具才能支撑起我们编写Java程序)

        当然有一个重要的组成部分——JRE(Java程序运行环境).

接下来就针对JRE做一个详细说明:

✨JRE:

        官方JRE(Java Runtime Environment) 是Java运行环境,包含了JVM和Java类库。JRE是运行Java程序所需的环境,它提供了Java程序运行所需的库文件和JVM。因此,如果你只需要运行Java程序,那么只需要安装JRE即可。        

        

需要区分的地方:

        这里再穿插一下IDEA是什么,IDEA是编写代码的平台,里面集成了一些好用的工具,例如预编译工具,能够在出现一些语法错误的时候提示我们,但是起到编译运行的还是JVM虚拟机!

⚽JVM内存模型:        

        首先在开头处需要声明一下: JVM内存模型与JMM内存模型不是一回事,JVM内存模型就是虚拟机内部的内存模型,但是JMM内存模型主是描述Java线程之间如如何工作的,如何做到线程之间共享变量的。

        JVM内存模型分为5个区,由这5个区合力搭建出JVM内存模型的。

程序计数器:记录了要执行的下一条指令的地址

栈:

        虚拟机栈:保存临时变量、参数、函数调用的关系

        本地方法栈:与虚拟机栈类似、底层是C或C++代码

堆:保存了new的对象

方法区(元数据区):保存了静态变量、常量、常量池等

        栈区、程序计数器是线程私有的

        堆区、方法区是线程共享的 

🏉类加载过程:

        了解了JVM内存模型,接下来需要了解.class文件在JVM读取与加载的过程。

        1.加载:读取.class文件中的内容

        2.验证:校验读取到的内容是否符合JVM规范。

        3.准备:为类的静态变量分配内存,并将其初始化为默认值。

        4.解析:将符号引用全部替换为直接引用

        5.初始化:此阶段是执行类构造器()方法的过程。此方法由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{}块)中的语句合并而成。当初始化一个类的时候,如果发现其父类还未进行初始化,则需要先触发其父类的初始化。Java虚拟机会保证一个类的()方法在多线程下被同步加锁。

⚾双亲委派模型:

        在类加载时,需要遵循双亲委派模型:

        如果一个类被请求加载时,首先不会自己加载,将加载的请求抛给父类,如果父类可以加载,自己就不再加载,如果父类不能加载,自己再进行加载。

优先级由高到低:标准库类加载器->扩展库类加载器->第三方库类加载器->自定义类加载器

        如下图:

双亲委派模型作用:       

1. 避免重复加载类: ⽐如 A 类和 B 类都有⼀个⽗类 C 类,那么当 A 启动时就会将 C 类加载起来,那么在 B 类进⾏加载时就不需要在重复加载 C 类了。
2. 安全性 :使⽤双亲委派模型也可以保证了 Java 的核⼼ API 不被篡改,如果没有使⽤双亲委派模型,⽽是每个类加载器加载⾃⼰的话就会出现⼀些问题,⽐如我们编写⼀个称为 java.lang.Object类的话,那么程序运⾏的时候,系统就会出现多个不同的 Object 类,⽽有些 Object 类⼜是用户自己提供的因此安全性就不能得到保证了。

🥎类初始化顺序(带有继承关系):

        父类静态代码块、静态变量->子类静态代码块、静态变量->父类普通变量->父类构造函数->子类普通方法->子类构造方法

🏀GC垃圾回收:

        首先需要搞清楚,哪里的垃圾需要回收,那里的垃圾不需要回收?        

        线程私有的区域中的垃圾我们不需要回收,当线程结束时,这些区域也会跟着清空

        线程共享的区域,如果线程结束,但是线程共享区中的引用对象不会随之消失。

        因此着重关注的是两个区域——堆区、方法区

        但是相较于创建的量和空间的消耗来说,堆区是垃圾回收的重灾区,但是方法区也有垃圾回收机制,但是不怎么关注。

🏐如何判断该对象是否是垃圾?

        如何判定堆区中的对象就是垃圾呢?有以下几种方法:

🍿1.引用计数法:

        采用计数的方式,如何一个对象被引用,此时计数器+1,如何不在引用该对象计数器的值就-1。

造成的问题:

        如果采用方法进行垃圾的判定,会造成"循环引用"的问题,例如:

class Student{
    private String name;
    public Student student;
    public Student(String name) {
        name = this.name;
    }
}
public class Test {
    public void circulation(){
        Student a = new Student("a");
        Student b = new Student("b");
        a.student = b;
        b.student = a;
        a = null;
        b = null;
    }
}

此时明明a、b两个引用已经断开指向a、b的对象,但是其内部还有相互的指向,没有办法被释放了。

🍕2.可达性分析:

        为了解决上述出现的问题,可以定期进行扫描,如果发现虽然被引用着,但是到达不了GCroots也会被视为垃圾,如下图:

                     

         也就是从GC Roots开始扫描,如果是能到达的对象此时构成了引用链,如果在某个对象在经过下次扫描不在这条引用链上了,就被视为"垃圾"。

   该方法被作为GC垃圾回收的首选方法。但是在这个过程中可能会造成STW(Stop The World),可以理解为时间停顿。

🥯垃圾回收方法:

        通过可达性分析以后判断出哪些对象是垃圾,接下来就来谈一谈这些垃圾是如何被回收的?

有以下方法:

🌭 1.标记回收:

        经过可达性分析以后,如果是垃圾,此时被标记一下。之后经过多轮检测之后,将标记的垃圾进行清除:

        

缺点:

        造成内存碎片化问题,此时在存储效率上会大减折扣。

🧇2.标记整理:

        在标记回收算法的基础上,进行改良,因为上述的做法会造成内存碎片问题,如果每次回收以后,对内存中存活的对象继续宁整理,整理在一起,此时内存空间可利用率会提高。

        

缺点:

        整理复制需要消耗大量的实践,时间效率降低。

🧈3.复制算法:

        如果我么每次将内存空间分为两部分,一部分空出来,另一部分使用,垃圾回收只需要对使用的一部分进行回收,进行标记,经过多轮之后,将存活的对象复制到另一块没有使用的内存当中。

 

缺点:

        如果存活的对象较多,此时复制时比较消耗时间。

🍞4.分代回收算法:

        将堆区中的区域划分为新生代与老年代

 

新生代又被划分为:伊甸区、幸存区

  • 堆内存为两个区:新生代 (Young) 和老年代 (Old);
  • 新生代默认占堆内存的 1/3,老年代默认占堆内存的 2/3;
  • 新生代又分为 Eden 区、Survivor From区、Survivor To区默认比例是 8:1:1   

过程:

        首次被创建的对象先进入伊甸区,之后经过可达性分析与复制算法,将幸存的对象放入幸存区,再次经多轮过可达性分析与复制算法,此时将最终存活的对象放入老年代。老年代中进行可达性分析的频次就比较低了。

😶常见的GC垃圾回收器:

        上述讲的都是一些垃圾回收的一些思路,这些GC垃圾回收器内部回收垃圾时都有用到上述的思路。

1️⃣1.GMS垃圾收集器:

        老年代收集器,并发GC,GMS是一种获取最短停顿时间(STW)为目标的收集器。

实现原理:

        其内部是基于标记-清除算法实现的。(具体大家可以自行了解)

由于整个过程中耗时最⻓的并发标记和并发清除过程收集器线程都可以与⽤⼾线程⼀起⼯作,所以,从总体上来说,CMS收集器的内存回收过程是与⽤⼾线程⼀起并发执⾏的。       

2️⃣2.G1垃圾收集器 :

        全区域的垃圾收集器,其主要的做法是将分代回收算法中的区域划分为更小的区域块,此时不再是一次性进行复制算法回收,只对一部分进行可达性分析+复制算法回收。

年轻代垃圾收集:
        在G1垃圾收集器中,年轻代的垃圾回收过程使⽤复制算法。把Eden区和Survivor区的对象复制到新的Survivor区域。
老年代垃收集:
对于⽼年代上的垃圾收集,G1垃圾收集器也分为4个阶段,基本跟CMS垃圾收集器⼀样,但略有不同:
初始标记 (Initial Mark)阶段 - G1需要暂停应⽤程序的执⾏,它会标记从根对象出发,在根对象的第⼀层孩⼦节点中标记所有可达的对象。但是G1的垃圾收集器的Initial Mark阶段是跟minor gc⼀同发⽣的。也就是说,在G1中,你不⽤像在CMS那样,单独暂停应⽤程序的执⾏来运⾏Initial Mark阶段,而是在G1触发minor gc的时候⼀并将年⽼代上的Initial Mark给做了。
并发标记 (Concurrent Mark)阶段 - 在这个阶段G1做的事情跟CMS⼀样。但G1同时还多做了⼀件事 情,就是如果在Concurrent Mark阶段中,发现哪些Tenured region中对象的存活率很⼩或者基本没有对象存活,那么G1就会在这个阶段将其回收掉,⽽不⽤等到后⾯的clean up阶段。这也是Garbage First名字的由来。同时,在该阶段,G1会计算每个 region的对象存活率,⽅便后⾯的clean up阶段使⽤ 。
最终标记 (CMS中的Remark阶段) - 在这个阶段G1做的事情跟CMS⼀样, 但是采⽤的算法不同,G1采⽤⼀种叫做SATB(snapshot-at-the-begining)的算法能够在Remark阶段更快的标记可达对象。
筛选回收( Clean up/Copy)阶段 - 在G1中,没有CMS中对应的Sweep阶段。相反 它有⼀个Cleanup/Copy阶段,在这个阶段中,G1会挑选出那些对象存活率低的region进⾏回收,这个阶段也是和minor gc⼀同发⽣的。

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

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

相关文章

cocos creator 笔记-路边花草

版本:3.8.5 实现目标:给3d道路生成路边景观花草 在场景下创建一个节点,我这里种植两种花草模型,兰花和菊花,所以分别在节点下另创建两个节点,为了静态合批。 1.将花草模型分别拖入场景中,制作…

Langchain中的表格解析:RAG 和表格的爱恨情仇

实现 RAG(Retrieval-Augmented Generation)是一个挑战,尤其是在有效解析和理解非结构化文档中的表格时。这在处理扫描文档或图像格式的文档时尤为困难。这些挑战至少包括以下三个方面: 1.表格的“叛逆期”:不准确的解析可能会破坏表格结构: 表格在文档里就像个叛逆的青少…

CAT1模块 EC800M HTTP 使用后续记录

记录一下 CAT1 模块EC800 HTTP 使用后续遇到的问题 by 矜辰所致目录 前言一、一些功能的完善1.1 新的交互指令添加1.2 连不上网络处理 二、问题出现三、分析及解决3.1 定位问题3.2 问题分析与解决3.2.1 查看变量在内存中的位置 3.3 数据类型说明3.3.1 常用格式化输出符号…

Python 标准库与数据结构

Python的标准库提供了丰富的内置数据结构和函数,使用这些工具能为我们提供一套强有力的工具。 需要注意的是,相比C与Java,Python的一些特点: Python不需要显式声明变量类型Python没有模板(Template)的概念,因为Pytho…

大疆上云api介绍

概述 目前对于 DJI 无人机接入第三方云平台,主要是基于 MSDK 开发定制 App,然后自己定义私有上云通信协议连接到云平台中。这样对于核心业务是开发云平台,无人机只是其中一个接入硬件设备的开发者来说,重新基于 MSDK 开发 App 工作量大、成本高,同时还需要花很多精力在无人…

2025-03-25 Unity 网络基础4——TCP同步通信

文章目录 1 Socket1.1 Socket 类型1.2 构造 Socket1.3 常用属性1.4 常用方法 2 TCP 通信2.1 服务端配置2.2 客户端配置2.3 进行通信2.4 多设备通信 3 区分消息 1 Socket ​ Socket 是 C# 提供的网络通信类(其它语言也有对应的 Socket 类),是…

C++进阶(一)

个人主页:PingdiGuo_guo 收录专栏:C干货专栏 前言 本篇博客是讲解函数的重载以及引用的知识点的。 文章目录 前言 1.函数重载 1.1何为函数重载 1.2函数重载的作用 1.3函数重载的实现 2.引用 2.1何为引用 2.2定义引用 2.3引用特性 2.4常引用 2…

深度解读DeepSeek:开源周(Open Source Week)技术解读

深度解读DeepSeek:开源周(Open Source Week)技术解读 深度解读DeepSeek:源码解读 DeepSeek-V3 深度解读DeepSeek:技术原理 深度解读DeepSeek:发展历程 文章目录 一、开源内容概览Day1:FlashMLAD…

AI Agent开发与应用

AI Agent开发与应用:本地化智能体实践——本地化智能体开发进展与主流框架分析 我要说的都在ppt里面了,相关复现工作请参考ai agent开发实例 OpenManus Dify Owl 第二个版本更新了对话的框架,通过gradio做了一个全新的界面 只测试了阿里云…

石斛基因组-文献精读122

A chromosome-level Dendrobium moniliforme genome assembly reveals the regulatory mechanisms of flavonoid and carotenoid biosynthesis pathways 《染色体水平的石斛基因组组装揭示了黄酮类和胡萝卜素生物合成途径的调控机制》 摘要 石斛(Dendrobium monil…

javaSE.多维数组

1 final 引用类型 final int[] arr 继承Object 的引用类型,不能改变引用的对象 存的其实是引用 数组类型数组,其实存的是引用 int [][] arr new int[][] { {1,2,3}, {4,5,6} };int [] a arr[0]; int [] b arr[1];

Python条件处理,新手入门到精通

Python条件处理,新手入门到精通 对话实录 **小白**:(崩溃)我写了if x 1:,为什么Python会报错? **专家**:(推眼镜)**是赋值,才是比较**!想判断相…

JPA实体类注解缺失异常全解:从报错到防御!!!

🚨 JPA实体类注解缺失异常全解:从报错到防御 🛡️ 一、💥 问题现象速览 // 经典报错示例 Caused by: java.lang.IllegalArgumentException: Not a managed type: class com.example.entity.Product典型症状: &…

【C语言】多进程/多线程

【C语言】多进程/多线程 参考链接多进程/多线程服务器1. 多进程服务器2. 多线程服务器 结语参考链接 参考链接 c 中文网 菜鸟 c 多进程/多线程服务器 多进程和多线程是常用的并发编程技术。它们都允许程序同时执行多个任务,提高了系统的资源利用率和程序的运行效率…

模糊数学 | 模型 / 集合 / 关系 / 矩阵

注:本文为来自 “模糊数学 | 模型及其应用” 相关文章合辑。 略作重排。 如有内容异常,请看原文。 模糊数学模型:隶属函数、模糊集合的表示方法、模糊关系、模糊矩阵 wamg 潇潇 于 2019-05-06 22:35:21 发布 1.1 模糊数学简介 1965 年&a…

QinQ项展 VLAN 空间

随着以太网技术在网络中的大量部署,利用 VLAN 对用户进行隔离和标识受到很大限制。因为 IEEE802.1Q 中定义的 VLAN Tag 域只有 12 个比特,仅能表示 4096 个 VLAN,无法满足城域以太网中标识大量用户的需求,于是 QinQ 技术应运而生。…

数据结构—树(java实现)

目录 一、树的基本概念1.树的术语2.常见的树结构 二、节点的定义三、有关树结构的操作1.按照数组构造平衡 二叉搜索树2.层序遍历树3.前、中、后序遍历树(1).前序遍历树(2).中序遍历树(3).后序遍历树(4).各种遍历的情况的效果对比 4.元素添加5.元素删除1.删除叶子节点2.删除单一…

S32K144外设实验(七):FTM输出多路互补带死区PWM

文章目录 1. 概述1.1 时钟系统1.2 实验目的2. 代码的配置2.1 时钟配置2.2 FTM模块配置2.3 输出引脚配置2.4 API函数调用1. 概述 互补对的PWM输出是很重要的外设功能,尤其应用再无刷电机的控制。 1.1 时钟系统 笔者再墨迹一遍时钟的设置,因为很重要。 FTM的CPU接口时钟为SY…

[网鼎杯 2020 白虎组]PicDown1 [反弹shell] [敏感文件路径] [文件描述符]

常见读取路径 /etc/passwd一些用户和权限还有一些乱七八糟的 /proc/self/cmdline包含用于开始当前进程的命令 /proc/self/cwd/app.py当前工作目录的app.py /proc/self/environ包含了可用进程的环境变量 /proc/pid/exe 包含了正在进程中运行的程序链接; /proc/pid…

单纯形法之大M法

1. 问题背景与标准化 在求解某些线性规划问题时,往往难以直接找到初始的基本可行解。特别是当约束中存在等式或 “≥” 类型的不等式时,我们需要引入人工变量来构造一个初始可行解。 考虑如下标准形式问题(假设为最大化问题)&am…