彻底搞懂JVM垃圾回收

jvm_garbage_collection

哈喽,大家好🎉,我是世杰

欢迎大家关注我的公众号『程序员世杰』获取更多后端技术干货🎉🎉!

本文我为大家介绍「JVM垃圾回收那些事

面试连环call

  1. 如何判断对象是否应被回收?
  2. finalize方法的实现机制是什么?
  3. 如何判断类应该被回收?
  4. 垃圾回收算法都有什么? 优缺点是什么?
  5. Minor GC, Major GC, Full GC 分别代表什么?
  6. JVM都有哪些内存分配策略?

1. 对象回收判断

1.1 引用计数算法

定义

一个对象被创建之后,系统会给这个对象初始化一个引用计数器,当这个对象被引用了,则计数器 +1,而当该引用失效后,计数器便 -1,直到计数器为 0,意味着该对象不再被使用了,则可以将其进行回收了。

存在问题

两个对象出现循环引用的情况下,此时引用计数器永远不为 0,导致无法对它们进行回收。

对象之间循环引用

1.2 可达性分析算法

定义

以 GC Roots 作为起始点进行搜索,一步步遍历找到和这个根对象具有引用关系的对象,然后再从这些对象开始继续寻找,能够到达到的对象都是存活的,不可达的对象可被回收。

image

在 Java 中 GC Roots 一般包含以下内容:

  • 虚拟机栈中引用的对象
  • 本地方法栈中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中的常量引用的对象

1.3 finalize()

对象可以被回收,就代表一定会被回收吗

即使在可达性分析法中不可达的对象,也并不是立刻回收。宣告一个对象死亡,至少要经历两次标记

第一次标记

如果对象进行可达性分析算法之后发现未与 GC Roots 引用链相连,那它将会第一次标记并且进行一次筛选。当对象没有覆盖 finalize () 方法、或者 finalize () 方法已经被 JVM 执行过,则判定为可回收对象。如果对象有必要执行 finalize () 方法,则被放入 F-Queue 队列中。稍后在 JVM 自动建立、低优先级的 Finalizer 线程(可能多个线程)中触发这个方法.

第二次标记

GC 对 F-Queue 队列中的对象进行二次标记。如果对象在 finalize () 方法中重新与引用链上的任何一个对象建立了关联,那么二次标记时则会将它移出 “即将回收” 集合。如果此时对象还没成功逃脱,那么只能被回收了。

注意 ⚠️:JDK9 版本及后续版本中各个类中的 finalize 方法会被逐渐弃用移除。忘掉它的存在吧!


2. 类回收判断

在大量使用反射、动态代理、CGLib 等 ByteCode 框架、动态生成 JSP 以及 OSGi 这类频繁自定义 ClassLoader 的场景都需要虚拟机具备类卸载功能,以保证不会出现内存溢出。

JVM方法区存储的是类信息,因此主要针对方法区进行类回收,那么如何判断一个类是无用的类的呢?

类需要同时满足下面 3 个条件才能算是 “无用的类”

  • 该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。
  • 加载该类的 ClassLoader 已经被回收。
  • 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

虚拟机可以对满足上述 3 个条件的无用类进行回收,这里说的仅仅是“可以”,而并不是和对象一样不使用就会必然被回收


3. 垃圾回收算法

3.1 标记-清除

将存活的对象进行标记,然后清理掉未被标记的对象。

这是一个非常基本的GC算法,它是现代GC算法的思想基础,分为标记和清除两个阶段:先把所有活动的对象标记(可达性分析法)出来,然后把没有被标记的对象统一清除掉

image

不足

  • 标记和清除过程效率都不高;
  • 会产生大量不连续的内存碎片,导致无法给大对象分配内存。

3.2 标记-整理

让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

标记—整理算法适用于存活对象较多的场合,它的标记阶段和标记-清除算法中的一样。整理阶段是将所有存活的对象压缩到内存的一端,之后清理边界外所有的空间。它的效率也不高。

image

3.3 复制

将内存划分为大小相等的两块,每次只使用其中一块,当这一块内存用完了就将还存活的对象复制到另一块上面,然后再把使用过的内存空间进行一次清理。

它比标记-清除算法要高效,但不适用于存活对象较多的内存,因为复制的时候会有较多的时间消耗。它的致命缺点是会有一半的内存浪费

image

现在的商业虚拟机都采用这种收集算法来回收新生代,但是并不是将新生代划分为大小相等的两块,而是分为一块较大的 Eden 空间和两块较小的 Survivor 空间,每次使用 Eden 空间和其中一块 Survivor。在回收时,将 Eden 和 Survivor 中还存活着的对象一次性复制到另一块 Survivor 空间上,最后清理 Eden 和使用过的那一块 Survivor。

3.4 分代收集

现在的商业虚拟机采用分代收集算法,它根据对象存活周期将内存划分为几块,不同块采用适当的收集算法。

一般将堆分为新生代和老年代。

  • 新生代使用: 复制算法
  • 老年代使用: 标记-清除 或者 标记-整理 算法

新生代(Young Generation)

所有新生成的对象首先都是放在新生代的。新生代的目标就是尽可能快速的收集掉那些生命周期短的对象。

  • 新生代内存分配一块较大的 Eden 空间和两块较小的 Survivor 空间(默认 Eden 和 Survivor 的大小比例是 8:1)。每次使用 Eden 和其中一块 Survivor 空间(当Eden内存满了就会把对象分配到Survivor区
  • 回收时将 EdenSurvivor 空间中存活的对象一次性复制到另一块 Survivor 空间上。最后清理掉 Eden 和使用过的 Survivor 空间。Survivor 区总有一个是空的。
  • 如果另一块 Survivor 空间没有足够内存来存放上一次新生代收集下来的存活对象,那么这些对象则直接进入老年代。
  • 新生代发生的GC也叫做Minor GC,MinorGC发生频率比较高(不一定等Eden区满了才触发)。

img

老年代(Old Generation)

  • 在新生代中经历了 N 次垃圾回收后仍然存活的对象,就会被放到老年代中。因此,可以认为老年代中存放的都是一些生命周期较长的对象
  • 内存比新生代也大很多(大概比例是1:2),当老年代内存满时触发 Major 或者 Full GC,发生频率比较低,老年代对象存活时间比较长,存活率标记高

4. Minor GC、Major GC、Full GC

  • Minor GC/Young GC:只是新生代的垃圾收集

  • Major GC/Old GC:只是老年代的垃圾收集

    • 目前,只有 CMS GC 会有单独收集老年代的行为
    • 很多时候 Major GC 会和 Full GC 混合使用,需要具体分辨是老年代回收还是整堆回收
  • Mixed GC:收集整个新生代以及部分老年代的垃圾收集

    • 目前只有 G1 GC 会有这种行为
  • Full GC:收集整个 Java 堆和方法区的垃圾

img

对于 Minor GC,其触发条件非常简单,当 Eden 空间满时,就将触发一次 Minor GC。而 Full GC 则相对复杂,有以下条件:

  1. 调用 System.gc()

  2. 老年代空间不足

  3. 空间分配担保失败

  4. JDK 1.7 及以前的永久代空间不足

  5. Concurrent Mode Failure

注意 ⚠️:Concurrent Mode Failure 是 CMS 特有行为,执行 CMS GC 的过程中同时有对象要放入老年代,而此时老年代空间不足(可能是 GC 过程中浮动垃圾过多导致暂时性的空间不足),便会报 Concurrent Mode Failure 错误,并触发 Full GC。


5. 内存分配策略

JVM为对象分配内存时,会有一些分配策略保证更好的使用和回收对象

5.1 对象优先在 Eden 分配

  • 大多数情况下,对象在新生代 Eden 区分配,当 Eden 区空间不够时,发起 Minor GC。

5.2 大对象直接进入老年代

  • 大对象是指需要连续内存空间的对象,最典型的大对象是那种很长的字符串以及数组。

  • 经常出现大对象会提前触发垃圾收集以获取足够的连续空间分配给大对象。

  • -XX:PretenureSizeThreshold,大于此值的对象直接在老年代分配,避免在 Eden 区和 Survivor 区之间的大量内存复制。

5.3 长期存活的对象进入老年代

  • 为对象定义年龄计数器,对象在 Eden 出生并经过 Minor GC 依然存活,将移动到 Survivor 中,年龄就增加 1 岁,增加到一定年龄则移动到老年代中。

  • -XX:MaxTenuringThreshold 用来定义年龄的阈值。

5.4 动态对象年龄判定

  • 虚拟机并不是永远地要求对象的年龄必须达到 MaxTenuringThreshold 才能晋升老年代,如果在 Survivor 中相同年龄所有对象大小的总和大于 Survivor 空间的一半,则年龄大于或等于该年龄的对象可以直接进入老年代,无需等到 MaxTenuringThreshold 中要求的年龄。

5.5 空间分配担保

  • 在发生 Minor GC 之前,虚拟机先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果条件成立的话,那么 Minor GC 可以确认是安全的。

  • 空间分配担保是为了确保在 Minor GC 之前老年代本身还有容纳新生代所有对象的剩余空间。

  • 如果空间分配担保失败则会进行 Full GC

参考文章

【006】十分钟搞懂 Java 垃圾回收

Java 垃圾回收基础知识

JVM垃圾回收详解(重点)

JVM之垃圾回收机制(GC)


更多惊喜

我还将定期分享:

  • 最新互联网资讯:让你时刻掌握行业动态。

  • AI前沿新闻:紧跟技术潮流,不断提升自我。

  • 技术分享与职业发展:助你在职业生涯中走得更远、更稳。

  • 程序员生活趣事:让你在忙碌的工作之余找到共鸣与乐趣。

关注回复【1024】惊喜等你来拿!

点击查看惊喜

敬请关注【程序员世杰】

点击关注程序员世杰

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

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

相关文章

触摸屏虚拟键盘组件 jQuery Virtual Keyboard使用 自定义键盘

如何在触摸设备上为输入域添加虚拟键盘? 一个插件可以解决这个问题,关键还支持高度自定义(git地址): GitHub - Mottie/Keyboard: Virtual Keyboard using jQuery ~ 官网地址:Virtual Keyboard 使用步骤&…

Photoshop

彩色转灰度:ctrlshiftu 背景转黑色: 魔术棒容差10 shift连选 shiftF5(填充)钢笔选择 路径 工作路径 将路径作为选区载入 点回图层 按ctrlx删除选区 待更新

如何找回误删的文件?4个常用文件恢复方法!

对于许多用户来说,误删文件是一种常见而令人懊恼的情况。恢复误删文件的重要性在于,它可以帮助用户找回宝贵的数据,避免因数据丢失带来的各种不便和损失。 如何找回不小心删除的文件? 误删数据不知道怎么恢复,会给我…

Spring MVC入门3

看完这篇博客你能学到什么 理解JSON的使用理解注解PathVariable理解解注解RequestPart理解cookie和Session的基本概念理解cookie和Session的区别 如果想真正掌握,还需要自己勤加练习。 正文 JSON JSON概念 JSON:JavaScript Object Notation 【JavaS…

传输层协议之UDP

1、端口号 我们在应用层创建的套接字,是需要通过bind()接口绑定我们的IP地址与端口号的,这是因为数据从传输层向上交付到应用层时,需要用端口号来查找特定的服务进程。一般在网络通信时,用IP地址标识一台主机,用端口号…

小米起诉“小米”商标侵权,索赔500万!

近日浙江丽水有家叫小米的公司,因为商标侵权被小米科技起诉索赔500万,需要变更企业名称,官网也不能用“小米智能大家居”等,还有其它的赔偿,普推知产商标老杨分析,“小米智能大家居”“小米”,后…

ArduPilot开源飞控之AP_Mount_Topotek

ArduPilot开源飞控之AP_Mount_Topotek 1. 源由2. 框架设计3. 重要函数3.1 动态过程3.1.1 AP_Mount_Topotek::update3.1.2 AP_Mount_Backend::calculate_poi 3.2 基础能力3.2.1 AP_Mount_Topotek::healthy3.2.2 AP_Mount_Topotek::has_pan_control 3.3 设备功能3.3.1 AP_Mount_T…

第一次构建一个对话机器人流程解析(一)

1.问答机器人的组成 1.1 问答机器人的组成结构图 2. 问答机器人的组成-机器人的个人属性 所谓的机器人一般具备有个人的属性,这些属性固定,形成了机器人的个人偏好 在实现过程中,此处使用一个xml配置文件,配置了机器人的个人年…

卡码网KamaCoder 99. 岛屿数量

题目来源&#xff1a;99. 岛屿数量 C题解&#xff1a;来源代码随想录 懒得自己写了。直接搬运。。。 1. 深度优先搜索&#xff1a;它的循环在main函数里&#xff0c;遍历每个位置&#xff0c;判断 是否为陆地 及 是否访问过。 #include <iostream> #include <vecto…

自养号测评补单:速卖通卖家如何打造爆款?

在跨境电商的激烈竞争中&#xff0c;速卖通卖家们为了打造店铺的爆款商品&#xff0c;不仅需要紧跟市场趋势&#xff0c;还需理解爆款商品的生命周期。以下是一些实用的策略&#xff0c;帮助卖家们更有效地打造爆款&#xff1a; 一、精准选品策略 面对全球多样化的消费者群体&…

算法复杂度<数据结构 C版>

什么是算法复杂度&#xff1f; 简单来说算法复杂度是用来衡量一个算法的优劣的&#xff0c;一个程序在运行时&#xff0c;对运行时间和运行空间有要求&#xff0c;即时间复杂度和空间复杂度。 目录 什么是算法复杂度&#xff1f; 大O的渐近表达式 时间复杂度示例 空间复杂度…

Open-TeleVision——通过VR沉浸式感受人形机器人视野的远程操作

前言 7.3日&#xff0c;我司七月「大模型机器人(具身智能)线下营」群里的一学员发了《Open-TeleVision: Teleoperation with Immersive Active Visual Feedback》这篇论文的链接&#xff0c;我当时快速看了一遍&#xff0c;还是有价值的一个工作(其有受mobile aloha工作的启发…

编程题目积累(day5)

题目&#xff1a; 源数组a&#xff0c;将a中所有元素乘以2之后添加进a&#xff0c;则这个a就叫双倍数组&#xff0c;给你一个数组a&#xff0c;判断它是不是双倍数组&#xff0c;如果是则输出源数组&#xff0c;不是则输出空数组。 补充知识&#xff1a; python中枚举和字典…

一个很变态但是有用的变现手段:用AI技术搞电商模特图,接单接到手软~

前言 今天带大家拆解一个特别有趣的项目&#xff0c;必须得跟大家分享一下&#xff1a;用AI技术搞电商模特图。 是不是感觉挺科幻的&#xff1f;但这真不是科幻小说里的情节&#xff0c;而是咱们现实生活中已经实现的事情。 想想看&#xff0c;咱们平常在网上看到的那些漂亮…

【RHCE】基于密钥的身份验证(Win-Linux)

目的&#xff1a;要提⾼系统安全性&#xff0c;通过在 OpenSSH 服务器上禁⽤密码⾝份验证来强制进⾏基于密钥的⾝份验证。 1、一台虚拟机无需密码连接另一台虚拟机 .ssh目录 > 保存了ssh相关的key和一些记录文件 &#xff08;1&#xff09;生成密钥对 使⽤这个流程在本地…

基于pytesseract的OCR图片识别

简介 pytesseract是基于谷歌的tesseract的OCR包&#xff0c;支持识别一些简单的数字、字母、中文。 安装 安装引擎 下载地址&#xff1a;https://digi.bib.uni-mannheim.de/tesseract/ 一般是Windows 64位系统最新版&#xff1a; 如果要识别中文&#xff0c;注意选中中文…

如何在Mac上恢复已删除的文件?

多数 Mac 用户在将 Mac 出售或赠送给其他用户之前会擦除数据。这样做是必要的&#xff0c;因为它有助于保护隐私并防止任何人滥用您的机密数据。在大多数情况下&#xff0c;您会故意抹掉数据和文件。但在某些情况下&#xff0c;你做错了。 大多数人可能认为文件擦除和文件删除…

微软新必应对Edge的“全面”开放及其市场影响

前言 微软新必应Bing Chat的全面开放&#xff0c;特别是针对Edge浏览器用户的功能&#xff0c;将带来一系列重要的变化和影响。新必应不仅集成了GPT-4的智能提问功能&#xff0c;还加入了DALLE的AI绘画技术。这些创新将极大地增强用户的搜索体验和交互方式&#xff0c;对AI应用…

一个不完全编译导致的奇怪问题

1. 初始代码 文件a.h: typedef struct {int a;int b; } a_t;void set_a(a_t *in, a_t *out); a.c: #include "a.h"void set_a(a_t *in, a_t *out) {out->a in->a;out->b in->b; } main.c: #include "a.h" #include <stdio.h>int…

排序(三)——归并排序(MergeSort)

欢迎来到繁星的CSDN&#xff0c;本期内容主要包括归并排序(MergeSort)的实现 一、归并排序的主要思路 归并排序和上一期讲的快速排序很像&#xff0c;都利用了分治的思想&#xff0c;将一整个数组拆成一个个小数组&#xff0c;排序完毕后进行再排序&#xff0c;直到整个数组排序…