Java - JVM内存模型及GC(垃圾回收)机制

JVM内存模型 

JVM堆内存划分(JDK1.8以前) 

JVM堆内存划分(JDK1.8之后)

主要变化在于:

  • java8没有了永久代(虚拟内存),替换为了元空间(本地内存)。
  • 常量池:1.7又把他放到了堆内存中;1.8之后出现了元空间,它又回到了方法区。

年轻代:

新生成的对象都放在年轻代,主要存放一些生命周期比较短的对象
新生代一般分三个区:一个Eden区,两个 Survivor区:
大部分对象在Eden区中生成,当Eden区满时,还存活的对象将被复制到Survivor区,当这个 Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当这个Survivor去也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制到老年代(Tenured)。
同时,根据程序需要,Survivor区是可以配置为多个的(多于两个),这样可以增加对象在年轻代中的存在时间,减少被放到年老代的可能。

老年代:
在年轻代中经历多次垃圾回收后仍存活的对象会被放入老年代中,一般存放生命周期较长的对象

java7永久代:
用于存放静态文件,如Java类、方法等。永久带对垃圾回收没有显著影响,一般不做垃圾回收,在JVM内存中划分空间。

java8元空间:
类似于永久带,不过它是直接使用物理内存而不占用JVM堆内存。


 垃圾回收机制

Java不用像C++一样自己释放内存,通过垃圾回收器GC来进行内存的回收释放。

不需要进行垃圾回收:程序计数器、JVM栈、本地方法栈。
需要进行回收垃圾的区:堆和方法区

方法区,也不太需要被回收, 它存放的是类对象, 主要工作就是 "类加载", 但是很少会涉及到 "类卸载", 需要 GC 但不迫切。堆,才是 GC工作的主战场, 很多 new 出来的对象, 用完之后, 就需要被及时回收!!

什么时候进行垃圾回收?

该类的所有实例对象都已经被回收。
加载该类的ClassLoader已经被回收。
该类对应的反射类java.lang.Class对象没有被任何地方引用。

几种垃圾收集器:

Minor GC:新生代GC,即发生在新生代(包括Eden区和Survivor区)的垃圾回收操作,当新生代无法为新生对象分配内存空间的时候,会触发Minor GC。因为新生代中大多数对象的生命周期都很短,所以发生Minor GC的频率很高,虽然它会触发stop-the-world,但是它的回收速度很快。


Major GC:Tenured区GC,用于回收老年代,出现Major GC通常会出现至少一次Minor GC。

Full GC:是针对整个新生代、老生代、元空间(metaspace,java8以上版本取代perm gen)的全局范围的GC。Full GC不等于Major GC,也不等于Minor GC+Major GC,发生Full GC需要看使用了什么垃圾收集器组合,才能解释是什么样的垃圾回收。

垃圾回收机制流程

 

GC主要处理的是年轻代与老年代的内存清理操作,元空间(永久代)一般很少用GC。具体流程如下:

① 当一个新对象产生,需要内存空间,为该对象进行内存空间申请。

② 首先判断Eden区是否有有内存空间,有的话直接将新对象保存在Eden区。

③ 如果此时Eden区内存空间不足,会自动触发MinorGC,将Eden区不用的内存空间进行清理,清理之后判断Eden区内存空间是否充足,充足的话在Eden区分配内存空间。

④ 如果执行MinerGC发现Eden区不足,判断存活区,如果Survivor区有剩余空间,将Eden区部分活跃对象保存在Survivor区,随后继续判断Eden区是否充足,如果充足在Eden区进行分配。

⑤ 如果此时Survivor区也没空间了,继续判断老年区,如果老年区空间充足,则将Survivor区中活跃对象保存到老年代,而后存活区有空余空间,随后Eden区将活跃对象保存在Survivor区之中,在Eden区为新对象开辟空间。

⑥ 如果老年代满了,此时将产生MajorGC进行老年代内存清理,进行完全垃圾回收。

⑦ 如果老年代执行MajorGC发现依然无法进行对象保存,此时会进行OOM异常(OutOfMemoryError)。

上面流程就是整个垃圾回收机制流程,总的来说,新创建的对象一般都会在Eden区生成,除非这个创建对象太大,那有可能直接在老年区生成。

 


如何判断一个对象是否为 "垃圾"

引用计数方案:

引用计数非 Java, 别的编程语言中使用的方法, 此处也简单介绍一下, 因为在 <<深入理解 Java 虚拟机>> 这本书中, 这两种方法都提到了.

"引用计数": 是使用额外的计数器, 记录某个对象, 被多少个引用指向. 如果某一时刻, 计数器为 0 了, 就说明此时没有引用指向它了, 此时这个对象就可以视为 "垃圾" 了.

引用计数就类似上图, 每新产生一个引用, 引用计数就 + 1, 每销毁一个引用, 引用计数就-1,

销毁可以理解为如果引用是局部变量, 出了作用域就是销毁了. 如果引用是成员变量, 则该引用对应的对象被销毁时, 引用本身才被销毁.

"引用计数" 的缺陷

1. 在多线程中, 需要修改同一个引用计数, 需要考虑到线程安全问题.

2有可能会造成不必要的开销!!  如果本身是大的对象, 多引入一个引用计数器, 负担不大; 如果本身是小的对象, 并且数量还多, 引入引用计数器就会造成不小的空间开销.

3. 可能会带来循环引用的问题 (最致命的)

有一个 Test 类, 类中有一个 Test 类型的成员变量,  操作1 是将这个类实例化 2 个对象, 操作 2 是分别将两个对象中的引用类型的成员变量互相指向对方.

此时计数器为 2, 当 a, b 对象某一时刻被销毁时: 

此时, 当前这俩对象引用计数都不为 0, 因此就都不会被当成垃圾, 但是这俩引用的地址都在对方手里, 就导致无法使用这俩对象, 最终就成了既不能被回收, 又不能被使用, 非常类似于 "死锁". 正因为引用计数有上述三个缺陷, 所以在 Java 中就不太合适, 就没有采取这个方案.

可达性分析

Java 中真正采用判断对象是否为垃圾的方案就是 "可达性分析".

可达性分析: 以代码中一些特殊的变量作为起点(GCRoot), 然后以起点出发, 判断哪些对象能够被访问到. 如果对象能被访问到, 就标记为 "可达", 当所有能被访问到的对象都被标记成 "可达" 时, 剩下的对象就是 "不可达" 的了, 就需要被当做垃圾回收了.

>>> 什么样的变量可以被称作 "起点" - GCRoot

1. 局部变量表中的引用. (栈里面的局部变量)

栈有多个, 每个线程一个栈, 每个栈里面有很多栈帧, 每个栈帧里面有一个自己的局部变量表. 所有线程的所有栈的所有栈帧的所有局部变量表中的所有变量, 都可以视为起点 - GCRoot.

2. 常量池中对应的对象.

3. 方法区中, 静态引用类型的成员.

>>> 什么叫做能够被访问到

对于二叉树而言, 只要记住根节点, 就能判断其他结点能否被访问到了.

1. 如果在代码中写了 root.right.right = null , 那么 F 结点就不可达了, 就可以被回收了.

2. 如果在代码中写了 root.right = null, 那么 C 结点就不可达了, 同时  F 结点也被一起带走了(回收).

>>> 可达性分析相较于引用计数解决了两个缺陷: 

1. 可达性分析不需要引用计数器, 没有占用额外的空间;

2. 可达性分析不会涉及到循环引用的问题.


几种垃圾回收算法

标记-清除算法(Mark-Sweep):最基础的GC算法,将需要进行回收的对象做标记,之后扫描,有标记的进行回收,这样就产生两个步骤:标记和清除。这个算法效率不高,而且在清理完成后会产生内存碎片,这样,如果有大对象需要连续的内存空间时,还需要进行碎片整理。

复制算法(Copying):新生代内存分为了三份,Eden区和2块Survivor区,保证有一块Survivor区是空闲的,这样,在垃圾回收的时候,将不需要进行回收的对象放在空闲的Survivor区,然后将Eden区和第一块Survivor区进行完全清理,这样有一个问题,就是如果第二块Survivor区的空间不够大怎么办?这个时候,就需要当Survivor区不够用的时候,暂时借持久代的内存用一下。此算法适用于新生代。

标记-整理(或叫压缩)算法(Mark-Compact):和标记-清楚算法前半段一样,只是在标记了不需要进行回收的对象后,将标记过的对象移动到一起,使得内存连续,这样,只要将标记边界以外的内存清理就行了。此算法适用于持久代。

分代回收算法

分代回收算法是通过区域划分, 实现不同区域和不同的垃圾回收策略, 从而实现更好的垃圾回收。

分带算法给对象引入了 "年龄" 的概念, 此处的年龄单位不是年, 而是对象活过 GC 的轮次. 对象刚创建出来, 没经历过 GC 的洗礼, 就认为年龄是 0, 没经过一轮 GC, 如果没被回收, 年龄就 + 1.

>>> 它是将 "复制算法" 和 "标记-整理算法" 的方案综合起来了, 只是根据对象存活周期的不同将内存划分为 "新生代" 和 "老年代" 两块大的区域. (具体要经过多少轮 GC 才会变成老年代, 我们不关心, 我们只关注策略本身.)


1. 新创建的对象都放在伊甸区中. 伊甸区中的对象有个特点: 绝大部分对象都活不过一轮 GC ,

基本上就是 "朝生夕死" .

2. 经过一轮 GC 的考验, 还存活下来的对象就通过 "复制算法" 放到了 "幸存区".

3. 幸存区中的对象又会经过下一轮 GC 的考验, 且每经过一轮 GC 都会淘汰一部分对象, 没被淘汰的对象, 就继续通过 "复制算法" 拷贝到下一个幸存区中. (在 2 号幸存区考验时, 存活的又复制到 1 号幸存区) 这个过程, 既保留了复制算法的高效, 无内存碎片的优势, 又避免了过多的浪费空间, 因为此处浪费的幸存区的空间, 它相对整体空间来说, 微乎其微.

4. 当对象在幸存区中经历了多轮 GC , 仍然没有被销毁, 就认为该对象一时半会不会被销毁, 于是就把这个对象拷贝到 "老年代" 了.

5. 老年代的对象, 也要经历 GC 的考验, 只是考验的频率大大降低了, 如果老年代的对象即将要被回收, 就是用 "标记-整理" 算法, 因为老年代对象被回收的频率不高, 就可以接受 "标记-整理" 算法带来的时间开销.

6. 还要注意的是: 有一个小的例外, 如果当前有一个特别大的对象, 就不经历上述分代回收过程, 直接就进入老年代, 因为大的对象在复制算法中, 是不太友好的.

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

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

相关文章

.NET Core 依赖注入 Microsoft.Extensions.DependencyInjection

文章目录 前言什么是依赖注入C# 使用依赖注入框架介绍 Microsoft.Extensions.DependencyInjectionNuget安装简单单例使用打印结果 自动装配举例自动装配测试用例打印结果自动装配执行顺序测试用例有歧义构造函数渐进式构造函数循环依赖 自动装配结论 手动装配手动注入别名注入 …

【AIGC】大语言模型的采样策略--temperature、top-k、top-p等

总结如下&#xff1a; 图片链接 参考 LLM解码-采样策略串讲 LLM大模型解码生成方式总结 LLM探索&#xff1a;GPT类模型的几个常用参数 Top-k, Top-p, Temperature

如何使用玻璃材质制作3D钻石模型

在线工具推荐&#xff1a; 3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.js AI自动纹理开发包 - YOLO 虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎 当谈到游戏角色的3D模型风格时&#xff0c;有几种不同的风格&#xf…

【Matlab算法】粒子群算法求解二维非线性优化问题(附MATLAB代码)

粒子群算法求解二维非线性优化问题 前言正文步骤分解代码可视化完整代码实现 前言 二维非线性优化问题是指在二维空间中寻找一个点&#xff0c;使得目标函数在该点取得最小&#xff08;或最大) 值&#xff0c;而这个目标函数是一个非线性函数。数学上&#xff0c;这类问题可以…

《Vue.js设计与实现》—Vue3响应系统的原理

一、响应式数据与副作用函数 1. 副作用函数 1-1 指令材料 在JavaScript中&#xff0c;副作用函数是指在执行过程中对外部环境产生可观察的变化或影响的函数。这种函数通常会修改全局变量、修改传入的参数、执行I/O操作&#xff08;如读写文件或发送网络请求&#xff09;、修…

计算目标检测和语义分割的PR

需求描述 实际工作中&#xff0c;相比于mAP项目更加关心的是特定阈值下的precision和recall结果&#xff1b;由于本次的GT中除了目标框之外还存在多边形标注&#xff0c;为此&#xff0c;计算IoU的方式从框与框之间变成了mask之间&#xff1b; 本文的代码适用于MMDetection下的…

gittee使用教学

一、git简介 Git是一个开源的分布式版本控制系统&#xff0c;用于敏捷高效的处理任何大小项目的版本管理。 核心功能&#xff1a; 项目的版本管理 团队协同开发 二、准备工作 1、下载 Git 2、除了选择安装位置以外&#xff0c;其他都无脑安装 3、检查一下安装情况 win…

C语言-每日刷题练习

[蓝桥杯 2013 省 B] 翻硬币 题目背景 小明正在玩一个“翻硬币”的游戏。 题目描述 桌上放着排成一排的若干硬币。我们用 * 表示正面&#xff0c;用 o 表示反面&#xff08;是小写字母&#xff0c;不是零&#xff09;&#xff0c;比如可能情形是 **oo***oooo&#xff0c;如果…

【从零开始学习JVM | 第三篇】类的生命周期(高频面试)

前言&#xff1a; 在Java编程中&#xff0c;类的生命周期是指类从被加载到内存中开始&#xff0c;到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。 在本文中&#xff0c;我们将深入探讨类的生命周期&#xff0c;从类加载到…

Docker Container(容器)——6

目录&#xff1a; 什么是容器&#xff1f;容器生活案例&#xff1f;为什么需要容器&#xff1f;容器的生命周期 容器 OOM容器异常退出容器暂停容器命令清单容器命令详解 docker createdocker rundocker psdocker logsdocker attachdocker execdocker startdocker stopdocker r…

用Sketch for Mac轻松创作无限可能的矢量绘图

在如今的数码时代&#xff0c;矢量绘图软件成为了许多设计师和创意爱好者的必备工具。而在众多的矢量绘图软件中&#xff0c;Sketch for Mac无疑是最受欢迎的一款。它以其简洁易用的界面和强大的功能&#xff0c;让用户能够轻松创作出无限可能的矢量图形。 首先&#xff0c;Sk…

Jmeter入门

一、下载jmeter 官网下载 下载之后解压&#xff0c;在目录/bin下面找到jmeter.bat双击之后即可启动Jmeter。 二、使用 如下左图&#xff0c;选择语言为中文&#xff0c;可以修改测试计划的名称。如下右图&#xff0c;添加线程组 添加线程组 添加http请求 路径传参方式 …

vue3-自定义组件的使用及传值!!!

1.在vue项目中创建一个自定义组件&#xff08;大多数页面中相同的样式&#xff0c;将其封装到组件中&#xff0c;可重复使用&#xff09; 2.将公共组件引入到你想使用的页面中 结果显示如下&#xff1a; 3.为公共组件传值 4.公共组件接收值&#xff0c;显示在组件上 注意事项&a…

wappalyzer基于插件的网站开发技术解析工具

一、wappalyzer 解释&#xff1a;这是一款强大的工具&#xff0c;其主要能提供一种快速、可靠地检测网站所使用技术栈的方法&#xff0c;也就说说&#xff0c;服务器发来的信息都会被它剖析&#xff0c;然后分析出前端的技术栈&#xff0c;有时后端所使用的技术栈如果网页特征…

串口通信(1)-硬件知识

本文讲解串口通信的硬件知识。让读者快速了解硬件知识&#xff0c;为下一步编写代码做基础。 目录 一、概述 二、串口通信分类 2.1信息的传送方向进行分类 2.2同步通信和异步通信 三、串口协议 3.1 RS232 3.1.1 电气特性 3.1.2 连接器的机械特性 3.1.3 连接类型 3.1…

2023全国职业院校技能大赛信息安全管理与评估正式赛(模块三CTF)

全国职业院校技能大赛高等职业教育组信息安全管理与评估 \任务书\ 模块三 网络安全渗透、理论技能与职业素养 极安云科专注技能竞赛&#xff0c;包含网络建设与运维和信息安全管理与评估两大赛项&#xff0c;及各大CTF&#xff0c;基于两大赛项提供全面的系统性培训&#xf…

Pipenv环境配置+Pytest运行

环境配置 使用Pipenv进行虚拟环境管理&#xff0c;Pipfile为依赖模块管理文件。 安装pipenv&#xff1a;brew install pipenv根项目根目录下执行命令创建虚拟环境&#xff1a; pipenv install在Pycharm中指定项目运行的虚拟环境 &#xff1a;File->Settings->Project:-…

有趣的数学 数学建模入门三 数学建模入门示例两例 利用微积分求解

一、入门示例1 1、问题描述 某宾馆有150间客房&#xff0c;经过一段时间的经营&#xff0c;该宾馆经理得到一些数据&#xff1a;如果每间客房定价为200元&#xff0c;入住率为55&#xff05;&#xff1b;定价为180元&#xff0c;入住率为65&#xff05;&#xff1b;定价为160元…

第 5 部分 — LLM中红队的深入分析:数学和实证方法

一、说明 大型语言模型 (LLM) 领域正在迅速发展&#xff0c;需要强大的红队策略来确保其安全性和可靠性。 红队是一种模拟对抗性攻击来识别漏洞的方法&#xff0c;需要对理论基础和实际应用有深入的了解。在这个分析中&#xff0c;我深入研究了复杂的数学模型&#xff0c;并提供…

【k8s】使用Finalizers控制k8s资源删除

文章目录 词汇表基本删除操作Finalizers是什么&#xff1f;Owner References又是什么&#xff1f;强制删除命名空间参考 你有没有在使用k8s过程中遇到过这种情况: 通过kubectl delete指令删除一些资源时&#xff0c;一直处于Terminating状态。 这是为什么呢&#xff1f; 本文将…