面试题:JVM的垃圾回收

一、GC概念

为了让程序员更专注于代码的实现,而不用过多的考虑内存释放的问题,所以,在Java语言中,有了自动的垃圾回收机制,也就是我们熟悉的GC(Garbage Collection)

有了垃圾回收机制后,程序员只需要关心内存的申请即可,内存的释放由系统自动识别完成。

在进行垃圾回收时,不同的对象引用类型,GC会采用不同的回收时机

换句话说,自动的垃圾回收的算法就会变得非常重要了,如果因为算法的不合理,导致内存资源一直没有释放,同样也可能会导致内存溢出的。

当然,除了Java语言,C#、Python等语言也都有自动的垃圾回收机制。

二、垃圾判定算法

在这里插入图片描述

简单一句就是:如果一个或多个对象没有任何的引用指向它了,那么这个对象现
在就是垃圾,如果定位了垃圾,则有可能会被垃圾回收器回收。

如果要定位什么是垃圾,有两种方式来确定:

  • 引用计数法
  • 可达性分析算法

💖 引用计数法

一个对象被引用了一次,在当前的对象头上递增一次引用次数,如果这个对象的
引用次数为0,代表这个对象可回收

在这里插入图片描述
在这里插入图片描述
当对象间出现了循环引用的话,则引用计数法就会失效

在这里插入图片描述
目前上方的引用关系和计数都是没问题的,但是,如果代码继续往下执行,如下

在这里插入图片描述
优点

  • 实时性高,无需等到内存不够的时候,才开始回收,运行时根据对象的计数器是否为 0,就可以直接回收
  • 在垃圾回收过程中,应用无需挂起。如果申请内存时,内存不足,则立刻报OOM错误
  • 区域性,更新对象的计数器时,只是影响到该对象,不会扫描全部对象。

缺点

  • 每次对象被引用时,都需要去更新计数器,有一点时间开销
  • 浪费 CPU 资源,即使内存任然够用,仍然在运行时进行计数器统计。
  • 无法解决循环引用问题,会引发内存泄露。

💖 可达性分析算法

现在的虚拟机采用的都是通过可达性分析算法来确定哪些内容是垃圾。

会存在一个根节点【GC Roots】,引出它下面指向的下一个节点,再以下一个节点节点开始找出它下面的节点,依次往下类推。直到所有的节点全部遍历完毕。

根对象是那些肯定不能当做垃圾回收的对象,就可以当做根对象
局部变量,静态方法,静态变量,类信息
核心是:判断某对象是否与根对象有直接或间接的引用,如果没有被引用,则可以当做垃圾回收

在这里插入图片描述
X,Y 这两个节点是可回收的,但是并不会马上的被回收!! 对象中存在一个方法【finalize】。当对象被标记为可回收后,当发生GC时,首先会判断这个对象是否执行了finalize方法,如果这个方法还没有被执行的话,那么就会先来执行这个方法,接着在这个方法执行中,可以设置当前这个对象与GC ROOTS产生关联,那么这个方法执行完成之后,GC会再次判断对象是否可达,如果仍然不可达,则会进行回收,如果可达了,则不会进行回收。

finalize方法 对于每一个对象来说,只会执行一次。如果第一次执行这个方法的时候,设置了当前对象与RC ROOTS关联,那么这一次不会进行回收。 那么等到这个对象第二次被标记为可回收时,那么该对象的finalize方法就不会再次执行了。

GC ROOTS:

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象
/**
 * demo是栈帧中的本地变量,当 demo = null 时,由于此时 demo 充当了 GC Root 的作用,demo与原来指向的实例 new Demo() 断开了连接,对象被回收。
 */
public class Demo {
    public static  void main(String[] args) {
    	Demo demo = new Demo();
    	demo = null;
    }
}
  • 方法区中类静态属性引用的对象
/**
 * 当栈帧中的本地变量 b = null 时,由于 b 原来指向的对象与 GC Root (变量 b) 断开了连接,所以 b 原来指向的对象会被回收,而由于我们给 a 赋值了变量的引用,a在此时是类静态属性引用,充当了 GC Root 的作用,它指向的对象依然存活!
 */
public class Demo {
    public static Demo a;
    public static  void main(String[] args) {
        Demo b = new Demo();
        b.a = new Demo();
        b = null;
    }
}
  • 方法区中常量引用的对象
/**
 * 常量 a 指向的对象并不会因为 demo 指向的对象被回收而回收
 */
public class Demo {
    
    public static final Demo a = new Demo();
    
    public static  void main(String[] args) {
        Demo demo = new Demo();
        demo = null;
    }
}
  • 本地方法栈中 JNI(即一般说的 Native 方法)引用的对象

三、JVM 垃圾回收算法

1. 标记清除算法

标记清除算法,是将垃圾回收分为2个阶段,分别是标记和清除

(1)根据可达性分析算法得出的垃圾进行标记

(2)对这些标记为可回收的内容进行垃圾回收

在这里插入图片描述

可以看到,标记清除算法解决了引用计数算法中的循环引用的问题,没有从root节点引用的对象都会被回收。

同样,标记清除算法也是有缺点的:

  • 效率较低,标记和清除两个动作都需要遍历所有的对象,并且在GC时,需要停止应用程序,对于交互性要求比较高的应用而言这个体验是非常差的。
  • 重要)通过标记清除算法清理出来的内存,碎片化较为严重,因为被回收的对象可能存在于内存的各个角落,所以清理出来的内存是不连贯的。

2. 复制算法(新生代)

复制算法的核心就是,将原有的内存空间一分为二,每次只用其中的一块,在垃圾回收时,将正在使用的对象复制到另一个内存空间中,然后将该内存空间清空,交换两个内存的角色,完成垃圾的回收。

如果内存中的垃圾对象较多,需要复制的对象就较少,这种情况下适合使用该方式并且效率比较高,反之,则不适合。

在这里插入图片描述

(1)将内存区域分成两部分,每次操作其中一个。

(2)当进行垃圾回收时,将正在使用的内存区域中的存活对象移动到未使用的内存区域。当移动完对这部分内存区域一次性清除。

(3)周而复始。

优点:

  • 垃圾对象多的情况下,效率较高
  • 清理后,内存无碎片

缺点:

  • 分配的2块内存空间,在同一个时刻,只能使用一半,内存使用率较低

3. 标记整理算法

标记整理(压缩)算法是在标记清除算法的基础之上,做了优化改进的算法。和标记清除算法一样,也是从根节点开始,对对象的引用进行标记,在清理阶段,并不是简单的直接清理可回收对象,而是将存活对象都向内存另一端移动,然后清理边界以外的垃圾,从而解决了碎片化的问题。

在这里插入图片描述

1)标记垃圾。

2)需要清除向右边走,不需要清除的向左边走。

3)清除边界以外的垃圾。

优缺点同标记清除算法,解决了标记清除算法的碎片化的问题,同时,标记压缩算法多了一步,对象移动内存位置的步骤,其效率也有有一定的影响。

与复制算法对比:复制算法标记完就复制,但标记整理算法得等把所有存活对象都标记完毕,再进行整理

四、分代收集算法

1. 概述

在java8时,堆被分为了两份:新生代和老年代【1:2】,在java7时,还存在一个永久代。

在这里插入图片描述

对于新生代,内部又被分为了三个区域。Eden区(伊甸园),S0区(幸存者区0),S1区【8:1:1】

当对新生代产生GC:MinorGC【young GC

当对老年代代产生GC:Major GC

当对新生代和老年代产生FullGC: 新生代 + 老年代完整垃圾回收,暂停时间长,应尽力避免

2. 工作机制

在这里插入图片描述

  • 新创建的对象,都会先分配到eden区

在这里插入图片描述

  • 当伊甸园内存不足,标记伊甸园与 from(现阶段没有)的存活对象

  • 将存活对象采用复制算法复制到 to 中,复制完毕后,伊甸园和 from 内存都得到释放

在这里插入图片描述

  • 经过一段时间后伊甸园的内存又出现不足,标记eden区域to区存活的对象,将存活的对象复制到from区

在这里插入图片描述

在这里插入图片描述

  • 当幸存区对象熬过几次回收(最多15次),晋升到老年代(幸存区内存不足或大对象会导致提前晋升

MinorGC、 Mixed GC 、 FullGC的区别是什么

在这里插入图片描述

  • MinorGC【young GC】发生在新生代的垃圾回收,暂停时间短(STW)

  • Mixed GC 新生代 + 老年代部分区域的垃圾回收,G1 收集器特有

  • FullGC新生代 + 老年代完整垃圾回收,暂停时间长(STW),应尽力避免?

名词解释:

STW(Stop-The-World):暂停所有应用程序线程,等待垃圾回收的完成

五、JVM 垃圾回收器

在jvm中,实现了多种垃圾收集器,包括:

  • 串行垃圾收集器

  • 并行垃圾收集器

  • CMS(并发)垃圾收集器

  • G1垃圾收集器

1. 串行垃圾回收器

Serial 和 Serial Old 串行垃圾收集器,是指使用单线程进行垃圾回收,堆内存较小,适合个人电脑

  • Serial 作用于新生代,采用复制算法

  • Serial Old 作用于老年代,采用标记-整理算法

垃圾回收时,只有一个线程在工作,并且java应用中的所有线程都要暂停(STW),等待垃圾回收的完成。

在这里插入图片描述

2. 并行垃圾回收器

Parallel NewParallel Old 是一个并行垃圾回收器,JDK8默认使用此垃圾回收器

  • Parallel New 作用于新生代,采用复制算法

  • Parallel Old 作用于老年代,采用标记-整理算法

垃圾回收时,多个线程在工作,并且java应用中的所有线程都要暂停(STW),等待垃圾回收的完成。

在这里插入图片描述

3. CMS(并发)垃圾回收器

CMS全称 Concurrent Mark Sweep,是一款并发的、使用标记-清除算法的垃圾回收器,该回收器是针对老年代垃圾回收的,是一款以获取最短回收停顿时间为目标的收集器,停顿时间短,用户体验就好。其最大特点是在进行垃圾回收时,应用仍然能正常运行。

在这里插入图片描述
在这里插入图片描述

4. G1垃圾回收器 ⭐

① 概述

  • 应用于新生代和老年代,在JDK9之后默认使用G1

  • 划分成多个区域,每个区域都可以充当 eden,survivor,old, humongous,其中 humongous 专为大对象准备

  • 采用复制算法

  • 响应时间与吞吐量兼顾

  • 分成三个阶段:新生代回收、并发标记、混合收集

  • 如果并发失败(即回收速度赶不上创建新对象速度),会触发 Full GC

在这里插入图片描述

② Young Collection(年轻代垃圾回收)

  • 初始时,所有区域都处于空闲状态

    在这里插入图片描述

  • 创建了一些对象,挑出一些空闲区域作为伊甸园区存储这些对象

    在这里插入图片描述

  • 当伊甸园需要垃圾回收时,挑出一个空闲区域作为幸存区,用复制算法复制存活对象,需要暂停用户线程

    在这里插入图片描述

    在这里插入图片描述

  • 随着时间流逝,伊甸园的内存又有不足

  • 将伊甸园以及之前幸存区中的存活对象,采用复制算法,复制到新的幸存区,其中较老对象晋升至老年代

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

③ Young Collection + Concurrent Mark (年轻代垃圾回收+并发标记)

当老年代占用内存超过阈值(默认是45%)后,触发并发标记,这时无需暂停用户线程

在这里插入图片描述

  • 并发标记之后,会有重新标记阶段解决漏标问题,此时需要暂停用户线程。

  • 这些都完成后就知道了老年代有哪些存活对象,随后进入混合收集阶段。此时不会对所有老年代区域进行回收,而是根据暂停时间目标优先回收价值高(存活对象少)的区域(这也是 Garbage First 名称的由来)。

    在这里插入图片描述

④ Mixed Collection (混合垃圾回收)

复制完成,内存得到释放。进入下一轮的新生代回收、并发标记、混合收集

在这里插入图片描述

其中H叫做巨型对象,如果对象非常大,会开辟一块连续的空间存储巨型对象

在这里插入图片描述

六、强软弱虚引用

💖 强引用

强引用:只有所有 GC Roots 对象都不通过【强引用】引用该对象,该对象才能被垃圾回收

User user = new User();

在这里插入图片描述

💖 软引用

软引用:仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次触发垃圾回收

User user = new User();
SoftReference softReference = new SoftReference(user);

在这里插入图片描述

💖 弱引用

弱引用:仅有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象

User user = new User();
WeakReference weakReference = new WeakReference(user);

在这里插入图片描述

延伸话题:ThreadLocal内存泄漏问题

ThreadLocal用的就是弱引用,看以下源码:

static class Entry extends WeakReference<ThreadLocal<?>> {
    Object value;

    Entry(ThreadLocal<?> k, Object v) {
         super(k);
         value = v; //强引用,不会被回收
     }
}

Entry 的 key 是当前 ThreadLocal,value 值是我们要设置的数据。

WeakReference表示的是弱引用,当JVM进行GC时,一旦发现了只具有弱引用的对象,不管当前内存空间是否足够,都会回收它的内存。但是value是强引用,它不会被回收掉

ThreadLocal使用建议:使用完毕后注意调用清理方法

💖 虚引用

虚引用:必须配合引用队列使用,被引用对象回收时,会将虚引用入队,由 Reference Handler 线程调用虚引用相关方法释放直接内存

在这里插入图片描述

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

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

相关文章

力扣2. 两数相加

Problem: 2. 两数相加 文章目录 题目描述思路复杂度Code 题目描述 思路 1.创建虚拟头节点dummy&#xff0c;用于存储相加的结果数字&#xff1b; 2.让指针p1、p2、tail分别指向l1、l2、dummy&#xff0c;定义int变量carry记录每次相加的进位值&#xff1b; 3.当p1不为空或者p2不…

双非计算机考研目标211,选11408还是22408更稳?

求稳得话&#xff0c;11408比22408要稳&#xff01; 很多同学只知道&#xff0c;11408和22408在考察的科目上有区别&#xff0c;比如&#xff1a; 11408考的是考研数学一和英语一&#xff0c;22408考察的是考研数学二和英语二&#xff1a; 考研数学一和考研数学二的区别大吗…

会话跟踪技术(Session 以及Cookie)

一: 前提概要 1>会话: 会话指的是用户打开浏览器, 访问某些web服务器资源的时候, 会话就会进行建立, 直到有一方断开, 那么会话才会结束, 需要注意的一点是, 一次的会话可以有多次的请求以及响应 2>会话跟踪: 是一种用于维护浏览器状态的方法, 服务器需要识别多次的请求,…

与鲸同行,智领未来!和鲸科技“人工智能+X”学科建设合作交流会(北京站)圆满结束!

在国家加快发展新质生产力的大背景下&#xff0c;3月25日下午&#xff0c;和鲸科技 2024 年“人工智能X”学科建设合作交流会&#xff08;北京站&#xff09;暨“AIX”实验室建设与供应商选型座谈会顺利召开。为提供更为集中和专业的讨论环境&#xff0c;本次会议特别采取闭门审…

Flink on Kubernetes (flink-operator) 部署Flink

flink on k8s 官网 https://nightlies.apache.org/flink/flink-kubernetes-operator-docs-release-1.1/docs/try-flink-kubernetes-operator/quick-start/ 我的部署脚本和官网不一样&#xff0c;有些地方官网不够详细 部署k8s集群 注意&#xff0c;按照默认配置至少有两台wo…

概率论经典题目-二维随机变量及分布--求离散型的联合分布律和边缘分布律问题

题目&#xff1a;一整数N等可能地在1,2,3,…,10十个值中取一个值设DD(N)是能整除N的正整数的个数,FF(N)是能整除N的素数的个数(注意1不是素数).试写出D和F的联合分布律,并求边缘分布律&#xff1f; 解答&#xff1a; 1&#xff09;要确定整数 N 能够被整除的正整数个数 D 和素…

Quiet-STaR:让语言模型在“说话”前思考

大型语言模型(llm)已经变得越来越复杂&#xff0c;能够根据各种提示和问题生成人类质量的文本。但是他们的推理能力让仍然是个问题&#xff0c;与人类不同LLM经常在推理中涉及的隐含步骤中挣扎&#xff0c;这回导致输出可能在事实上不正确或缺乏逻辑。 考虑以下场景:正在阅读一…

可重复不限数量结构数列的演化

有一个6*6的平面&#xff0c;这个平面的行和列可以自由的变换&#xff0c;在这个平面上有一个4点结构数列 按照8&#xff0c;13&#xff0c;5&#xff0c;8的顺序排列。让这个数列按照4-5-4的方式演化 这个数列很快收敛,收敛顺序为13&#xff0c;8&#xff0c;8&#xff0c;5 8…

Revit文件版本查看小工具

最近群里和私信的时候&#xff0c;经常有小伙伴询问如何不打开Revit查看Revit文件的版本。 习惯性的&#xff0c;第一思路是打开Dynamo&#xff0c;但是第一反应还需要先开Revit。 另外呢&#xff0c;群里小伙伴说优比的插件也可以。 总之呢&#xff0c;都需要一些工具&#xf…

对接中泰极速行情 | DolphinDB XTP 插件使用教程

XTP 是中泰证券推出的高性能交易平台&#xff0c;专为专业投资者提供高速行情及交易系统&#xff0c;旨在提供优质便捷的市场接入通道。目前支持股票、基金、ETF、债券、期权等多个市场&#xff0c;可满足不同投资者需求。 基于 XTP 官方 C SDK&#xff0c;DolphinDB 开发了 X…

【IDEA】使用debug方式去运行java程序

什么是debug工具&#xff1f; 调试工具&#xff08;debug工具&#xff09;是一种用于帮助程序员识别和修复程序中的错误的工具。它们提供了一系列的功能&#xff0c;帮助程序员在代码执行的过程中跟踪和检测问题&#xff0c;例如查看变量的值、检查函数的调用栈、设置断点来停…

算法学习——LeetCode力扣动态规划篇2

算法学习——LeetCode力扣动态规划篇2 343. 整数拆分 343. 整数拆分 - 力扣&#xff08;LeetCode&#xff09; 描述 给定一个正整数 n &#xff0c;将其拆分为 k 个 正整数 的和&#xff08; k > 2 &#xff09;&#xff0c;并使这些整数的乘积最大化。 返回 你可以获得…

java: 找不到符号 符号: 变量 log

在以下位置加上该配置"-Djps.track.ap.dependenciesfalse"

文件操作(随机读写篇)

1. 铺垫 建议先看&#xff1a; 文件操作&#xff08;基础知识篇&#xff09;-CSDN博客 文件操作&#xff08;顺序读写篇&#xff09;-CSDN博客 首先要指出的是&#xff0c;本篇文章中的“文件指针”并不是指FILE*类型的指针&#xff0c;而是类似于打字时的光标的东西。 打…

C++:类的6大默认成员函数:赋值运算符重载

文章目录 赋值运算符重载1.1 运算符重载的引用1.2 运算符重载的概念1.3 赋值运算符重载总结一下(赋值运算符) 赋值运算符重载 1.1 运算符重载的引用 有一个日期类Date: class Date { public:Date(int year 1900, int month 1, int day 1){_year year;_month month;_da…

Rust使用feature特性和条件编译,以及常用feature使用说明

Cargo Feature 是非常强大的机制&#xff0c;可以为大家提供条件编译和可选依赖的高级特性&#xff0c;可以为你省下不少的代码量来判断操作系统和条件编译等功能。rust官方条件编译文档&#xff1a;Conditional compilation - The Rust Reference features特性 Featuure 可以…

GeometryInstance点击改变颜色

目录 项目地址实现效果核心代码 项目地址 https://github.com/zhengjie9510/webgis-demo 实现效果 核心代码 // Draw different instances each with a unique color const rectangleInstance new Cesium.GeometryInstance({geometry: new Cesium.RectangleGeometry({recta…

EFCore的空迁移(EFCore操作已存在的数据库表,不影响其中的数据)

背景&#xff1a;EFCore默认的会自动创建数据表&#xff0c;但是有时又是DBFirst&#xff0c;数据库写好了要用现成的表。这个时候就需要进行一些特殊的操作了 1、写出跟要对接数据库的实体类 比如我的表是这样创建的 create table mail_test (user_id bigint auto_increment …

【Entity Framework】EF中DbSet类详解

【Entity Framework】EF中DbSet类详解 文章目录 【Entity Framework】EF中DbSet类详解一、概述二、定义DbSet2.1 具有DbSet属性的DbContext2.2 具有 IDbSet 属性的 DbContext 2.3 具有 IDbSet 属性的 DbContext三、DbSet属性四、DbSet方法五、DbContext动态生成DbSet 一、概述 …

医院消防巡检系统革新:凡尔码平台二维码技术引领安全升级

医院消防巡检&#xff0c;传统依赖手工记录&#xff0c;效率和准确性受限。凡尔码平台的二维码消防巡检系统&#xff0c;以创新技术颠覆传统&#xff0c;实现即时、精准的安全管理&#xff0c;确保医院消防安全无虞。 凡尔码平台的消防巡检系统不仅提升了医院安全管理的效率&a…