【Java校招面试】基础知识(五)——GC

news2024/9/29 21:26:57

目录

  • 前言
  • 一、基础概念
  • 二、垃圾回收算法
  • 三、垃圾收集器
  • 四、引用
  • 后记


前言

本篇主要介绍Java垃圾回收机制——GC的相关内容。

“基础知识”是本专栏的第一个部分,本篇博文是第五篇博文,如有需要,可:

  1. 点击这里,返回本专栏的索引文章
  2. 点击这里,返回上一篇《【Java校招面试】基础知识(四)——JVM》

一、基础概念

01. 垃圾回收(GC)发生的位置
垃圾回收发生在JVM内存模型的堆内存

02. 对象被判定为垃圾的标准
没有被其他对象引用。

03. 判定对象是否为垃圾的算法
1) 引用计数算法: 通过判断对象的引用数量来决定对象是否可以被回收。每个对象实例都有一个引用计数器,被引用则+1,完成引用则-1。任何引用计数为0的对象实例可以被当作垃圾收集。

  • 优点: 执行效率高,程序执行受影响小。
  • 缺点: 无法检测出循环引用的情况,会导致内存泄漏。

2) 可达性分析算法: 通过判断对象的引用链是否可达来决定对象是否可以被回收;
在这里插入图片描述
04. 可作为GC Root的对象
1) 虚拟机栈中引用的对象(栈帧中的本地变量表);
2) 方法区中的常量引用对象;
3) 方法区中的类静态属性引用的对象;
4) 本地方法栈中JNI(Native方法)的引用对象;
5) 活跃线程的引用对象。


二、垃圾回收算法

01. 标记清除算法(Mark and Sweep)

  • 标记: 从根集合进行扫描,对存活的对象进行标记
  • 清除: 对堆内存从头到尾进行线性遍历,回收不可达对象内存
    在这里插入图片描述
  • 存在的问题: 产生大量不连续的碎片,造成堆内存碎片化

02. 复制算法(Copying)
将堆内存按照一定的比例分成两块或多块,分为对象面和空闲面。选择其中的一块或两块作为对象面,其他的作为空闲面。对象主要在对象面上创建。当对象面的内存用完时,将存活的对象从对象面复制到空闲面。然后将对象面多有对象内存清除。适用于对象存活率低的场景。
在这里插入图片描述

  • 优点:
    1) 解决了碎片化的问题;
    2) 顺序分配内存,简单高效;
    3) 适用于对象存活率较低的场景;
  • 缺点: 在对象存活率较高的场景中,需要进行较多的复制

03. 标记整理算法(Compacting)

  • 标记: 从根集合进行扫描,对存活的对象进行标记
  • 清除: 移动所有存活的对象,且按照内存地址次序依次排列,然后将末端内存地址以后的内存全部回收。
    在这里插入图片描述
    优点:
    1) 避免内存的不连续性;
    2) 不用设置两块内存互换;
    3) 适用于对象存活率较高的场景;

04. 分代收集算法(Generational Collector)——目前的主流垃圾回收算法

  • 特点:
    1) 垃圾回收算法的组合;
    2) 按照对象生命周期的不同划分区域以采取不同的垃圾回收算法;
  • 目的: 提高JVM的回收效率

05. JDK 8中对堆内存所作的调整
1) JDK 8以前及JDK 8的JVM内存模型区别
在这里插入图片描述
从图中可见,JDK 8中,从堆内存中去除了Permanent Generation(永久代),取而代之的是在本地内存中新设的Meta Space(元空间)

2) 永久代中存放的数据
   类的元数据(如类的名称、访问修饰符、父类、实现的接口等)
   常量池
   静态变量
   即时编译器编译后的代码
   一些需要在类加载时完成的初始化操作

3) 永久代存在的问题(被元空间替代的原因)
  ① 内存限制问题: 永久代的大小是通过-XX:MaxPermSize参数来指定的,而该参数的默认值较小,容易导致OutOfMemoryError异常。
  ② 类加载器泄漏问题: 在永久代中,存在着一些类加载器无法被回收的情况,导致永久代的内存无法释放,最终也会导致OutOfMemoryError异常。
  ③ 字符串常量池溢出问题: 由于字符串常量池被存储在永久代中,如果不合理地使用字符串常量,就会导致永久代的内存快速耗尽,最终也会导致OutOfMemoryError异常。
  ④ 性能问题: 永久代的垃圾回收效率很低,会导致应用程序的性能下降。
  ⑤ 维护成本问题: 永久代需要手动调整大小,而且需要进行垃圾回收,增加了应用程序的维护成本。

4) 永久代和元空间的区别
  ① 存储位置: 永久代位于JVM堆内存中,而元空间则是位于本地内存中。
  ② 大小限制: 永久代的大小是固定的,而且它很容易被填满,导致OutOfMemoryError。而元空间的大小是动态的,它可以根据需要来自动调整大小,因此更加灵活。
  ③ 垃圾回收: 永久代是由JVM的垃圾回收机制来进行管理和清除的。而元空间不再使用垃圾回收机制进行管理,而是使用本地内存的机制,例如使用操作系统的虚拟内存来管理。
  ④ 移植性: 由于永久代是Java虚拟机的一部分,因此它只能在Java虚拟机中使用。而元空间是本地内存的一部分,因此它可以在不同的虚拟机和操作系统中使用。

06. 垃圾回收器的分类

  • Minor GC: 针对年轻代的垃圾回收,通常情况下只会回收年轻代中的Eden区和一个Survivor区。
  • Major GC: 针对老年代的垃圾回收,通常情况下会回收整个老年代。
  • Full GC: 针对整个堆空间的垃圾回收,包括年轻代和老年代。

他们的区别主要有以下几点:
1) 发生频率不同: Minor GC频率很高,可能会发生多次,Major GC和Full GC发生频率较低。
2) 对应用程序的影响不同: Minor GC对应用程序的影响较小,Major GC和Full GC会导致较长的停顿时间,对应用程序的影响较大。
3) 回收效率不同: Minor GC的回收效率较高,Major GC和Full GC的回收效率较低。

07. 触发Full GC的条件(答出3点即可)
1) 老年代空间不足
2) 永久代空间不足(针对JDK 7及以下版本)
3) CMS GC时出现promotion failedconcurrent mode failure
4) Minor GC晋升到老年代的平均大小大于老年代的剩余空间
5) 程序中手动调用System.gc()
6) 使用RMI(远程方式)来进行RPC或管理的JDK应用,每小时执行1次Full GC

08. 年轻代
在这里插入图片描述
用于存放并尽可能快速地收集掉那些生命周期短的对象
1) 包含一个Eden(伊甸园)区和两个Suivivor(一个From区,一个To区),比例为8:1:1
2) 每次将Eden和From中的存活对象复制到To区中,最后清理掉Eden和From区;
3) 每次复制,存活对象的年龄+1,当对象的年龄达到一定值(默认是15,可以通过-XX:MaxTenuringThreshold修改)时,对象将会被移动到老年代。

09. 老年代
存放生命周期较长的对象,占整个堆内存的2/3
在这里插入图片描述
10. 常见的GC调优参数

  • -XX:SurvivorRatio, Eden和Suivivor的比值,默认8:1;
  • -XX:NewRatio, 老年代和年轻代内存大小的比例;
  • -XX:MaxTenuringThreshold, 对象聪年轻代晋升到老年代经过GC次数的最大阈值;

11. Stop-The-World
1) JVM由于要执行GC而停止了应用程序的执行;
2) 任何一种GC算法中都会发生;
3) 多数GC优化通过减少Stop-The-World发生的时间来提高程序性能。

12. SafePoint
1) 分析过程中对象引用关系不会发生变化的点;
2) 产生SafePoint的地方: 方法调用、循环跳转、异常跳转等。GC发生时,让所有的线程都跑到最新的安全点,如果发现线程不在安全点,则恢复线程,等其跑到安全点。
3) 安全点数量需要适中,太少会让GC等待太长的时间;太多会增加程序运行的负荷。


三、垃圾收集器

01. 新生代常见的垃圾收集器
1) Serial收集器(-XX:+UseSerialGC,复制算法)
在这里插入图片描述

  • 单线程收集,进行垃圾收集时,必须暂停所有工作线程;
  • 简单高效,Client模式下默认的年轻代收集器。

2) ParNew收集器(-XX:+UseParNewGC,复制算法)
在这里插入图片描述

  • 多线程收集,其余的行为、特点和Serial收集器一样;
  • 单核执行效率不如Serial,在多核下执行才有优势,默认开启的线程数与CPU核数相同;
  • 在Server模式下重要的收集器,除Serial外,只有ParNew可以和CMS收集器配合工作。

3) Parallel Scavenge收集器(-XX:+UseParallelGC,复制算法)
在这里插入图片描述
吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间)

  • 比起关注用户线程停顿时间,更关注系统吞吐量;
  • 在多核下执行才有优势,Server模式下默认的年轻代收集器;
  • 如果对于垃圾收集器运作原理不是很了解,在优化过程中遇到困难时,可以使用-XX:+UseAdaptiveSizePolicy把调优任务交给虚拟机。

02. 老年代常见的垃圾收集器
1) Serial Old收集器(-XX:+UseSerialOldGC,标记整理算法)
在这里插入图片描述

  • 单线程收集,进行垃圾收集时,必须暂停所有工作线程;
  • 简单高效,Client模式下默认的老年代收集器。

2) Parallel Old收集器(-XX:+UseParallelOldGC,标记整理算法)
多线程,吞吐量优先
在这里插入图片描述
3) CMS收集器(-XX:+UseConcMarkSweepGC,标记清除算法)
在这里插入图片描述

  • 初始标记:Stop-The-World
  • 并发标记:并发追溯标记,程序不会停顿
  • 并发预清理:查找执行并发标记阶段从年轻代晋升到老年代的对象
  • 重新标记:暂停虚拟机,扫描CMS堆中的剩余对象
  • 并发清理:清理垃圾对象,程序不会停顿
  • 并发重置:重置CMS收集器的数据结构

存在的问题: 采用标记清除算法,不对内存进行压缩,会造成空间碎片化的问题

03. 既可用于老年代也可用于年轻代的GC
G1(Garbage First)收集器(-XX:+UseG1GC,复制+标记整理算法)
在这里插入图片描述
1) 特点:

  • 并行和并发
  • 分代收集
  • 空间整合
  • 可预测的停顿

2) 设计:

  • 将整个Java堆内存划分成多个大小相等的Region
  • 年轻代和老年代不再物理隔离

04. 所有垃圾收集器的关系
在这里插入图片描述
05. 两个在新的垃圾收集器
这两个垃圾收集器在JDK 11中作为实验特性引入,在JDK 15中已成为正式的垃圾收集器。

  • Epsilon GC
  • ZGC

06. Object类的finalize()方法的作用和C++的析构函数有什么不同?
1) C++中的析构函数是在对象生命周期结束时自动调用的,而不是在垃圾回收时调用的。
2) C++中的析构函数可以手动调用,而Java中的finalize()方法只能由垃圾回收器调用。
3) C++中的析构函数可以被重载,而Java中的finalize()方法是Object类的一个标准方法,不能被重载。


四、引用

01. Java中的强引用,软引用,弱引用,虚引用都是什么?
强引用(Strong Reference):
1) 最普遍的引用: Object obj = new Object();
2) 抛出OutOfMemoryError终止程序也不会回收具有强引用的对象;
3) 通过将对象的引用设置为null来弱化引用,使其被回收。

软引用(Soft Reference):
1) 对象处在有用但非必须的状态;
2) 只有当内存空间不足时,GC才会回收该引用的对象的内存;
3) 可以用来实现高速缓存;
4) 用法

	String str = new String("abc");
	SoftReference<String> softReference = new SoftReference<>(str);

弱引用(Weak Reference):
1) 非必需的对象,比软引用更弱一些;
2) GC时会被回收;
3) 由于GC线程优先级很低,被回收的概率也不大;
4) 适用于引用偶尔被使用且不影响垃圾收集的对象;
5) 用法:

	String str = new String("abc");
	WeakReference<String> weakReference = new weakReference<>(str);

虚引用(Phantom Reference):
1) 不会决定对象的生命周期;
2) 任何时候都可能被垃圾收集器回收;
3) 跟踪对象被垃圾收集器回收的活动,起哨兵的作用;
4) 必须和引用队列ReferenceQueue联合使用,因为它的get函数返回值永远为null,因此无法用get函数判断它是否已被回收;
5) 用法:

	String str = new String("abc");
	ReferenceQueue refQueue = new ReferenceQueue();
	PhantomReference ref = new PhantomReference(str, refQueue);

02. 四种引用的比较
强引用 > 软引用 > 弱引用 > 虚引用

引用类型被回收的时间用途生存时间
强引用从来不会对象的一般状态JVM停止运行时终止
软引用内存不足时对象缓存内存不足时终止
弱引用垃圾回收时对象缓存GC运行后终止
虚引用未知/瞬间标记、哨兵未知/瞬间

03. 引用队列(ReferenceQueue)

  • 无实际存储结构,存储逻辑依赖于内部节点之间的关系来表达;
  • 存储关联的且被GC的软引用、弱引用和虚引用。

04. 引用队列的代码示例
1) 目标对象MyObject

	public class MyObject {
	    public String name;
	    public MyObject(String name){
	        this.name = name;
	    }
	
	    @Override
	    protected void finalize() throws Throwable {
	        System.out.printf("Finalizing Object %s", name);
	        super.finalize();
	    }
	}

2) 目标对象弱引用MyObjectWeakReference

	public class MyObjectWeakReference extends WeakReference<MyObject>{
	    public String name;
	    public MyObjectWeakReference(MyObject referent, ReferenceQueue<MyObject> q) {
	        super(referent, q);
	        this.name = referent.name;
	    }
	}

3) 调用类

	public class ReferenceQueueTest {
	    private static ReferenceQueue<MyObject> q = new ReferenceQueue<>();
	    private static void checkQueue() {
	        Reference ref;
	        while ((ref = (Reference<MyObject>) q.poll()) != null) {
	            System.out.println("In Queue: Weak Reference of " + ((MyObjectWeakReference) ref).name);
	            System.out.println("Reference Object " + ref.get());
	        }
	    }
	
	    public static void main(String[] args) {
	        List<WeakReference<MyObject>> refList = new ArrayList<>();
	        for (int i = 0; i < 3; i++) {
	            refList.add(new MyObjectWeakReference(new MyObject("MyObject" + i), q));
	            System.out.println("Created Weak Reference " + refList.get(i));
	        }
	        System.out.println("First Time Check.");
	        checkQueue();
	        System.gc();
	        try {Thread.sleep(1000);} catch (InterruptedException ie) {}
	        System.out.println("Second Time Check.");
	        checkQueue();
	    }
	}

4) 代码流程分析
i) 创建3个弱引用对象;
ii) 输出引用队列中的所有引用对象;
iii) 手动gc并等待其结束(这里是让线程睡眠1秒);
iv) 再次输出引用队列中的所有引用对象。

5) 代码输出

Created Weak Reference referencequeuetest.MyObjectWeakReference@15db9742
Created Weak Reference referencequeuetest.MyObjectWeakReference@6d06d69c
Created Weak Reference referencequeuetest.MyObjectWeakReference@7852e922
First Time Check.
Finalizing Object MyObject2
Finalizing Object MyObject1
Finalizing Object MyObject0
Second Time Check.
In Queue: Weak Reference of MyObject2
Reference Object null
In Queue: Weak Reference of MyObject1
Reference Object null
In Queue: Weak Reference of MyObject0
Reference Object null

6) 代码输出解析
i) 创建3个弱引用对象;
ii) 首次检查时,因为还没有发生GC,所以引用队列里什么都没有,没有任何输出;
iii) GC发生,MyObject的finalize方法被调用;
iv) 第二次检查时,因为已经发生GC,所以3个MyObjectWeakReference都在RefernceQueue里;
v) 因为已经发生GC,所以通过ref.get()得到的对象为null;


后记

GC(垃圾收集)部分内容也不少,我将这些知识点分为基础概念垃圾回收算法垃圾收集器引用4个部分,便于阅读和查找。

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

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

相关文章

【重新定义matlab强大系列五】函数filloutliers检测并替换数据中的离群值

&#x1f517; 运行环境&#xff1a;matlab &#x1f6a9; 撰写作者&#xff1a;左手の明天 &#x1f947; 精选专栏&#xff1a;《python》 &#x1f525; 推荐专栏&#xff1a;《算法研究》 #### 防伪水印——左手の明天 #### &#x1f497; 大家好&#x1f917;&#x1f91…

PyCharm下载、安装、注册以及简单使用【全过程讲解】

在使用PyCharm IDE之前&#xff0c;请确保自己的计算机里面安装了Python解释器环境&#xff0c;若没有下载和安装可以看看我之前的文章>>>Python环境设置>>>或者还可以观看视频讲解。 注意&#xff1a;本文软件的配置方式仅供个人学习使用&#xff0c;如有侵…

有研究员公开了一个解析并提取 Dell PFS BIOS 固件的工具(下)

导语&#xff1a;研究员公开了一个解析并提取 Dell PFS BIOS 固件的工具。 Apple EFI IM4P分配器 介绍 解析苹果多个EFI固件.im4p文件&#xff0c;并将所有检测到的EFI固件分割为单独的SPI/BIOS映像。 使用 你可以拖放或手动输入包含Apple EFI IM4P固件的文件夹的完整路径。…

【VM服务管家】VM4.2平台SDK_6.3 控件嵌入类

目录 2.3.1 渲染控件&#xff1a;渲染控件上自定义图形的方法2.3.2 参数控件&#xff1a;参数配置控件绑定模块的方法2.3.3 控件颜色&#xff1a;控件颜色修改方法2.3.4 独立控件&#xff1a;二次开发单独显示Group的方法2.3.5 取流控件&#xff1a;实时取流控件的使用方法2.3.…

【2023年第十一届泰迪杯数据挖掘挑战赛】B题:产品订单的数据分析与需求预测 23页论文及实现代码

【2023年第十一届泰迪杯数据挖掘挑战赛】B题&#xff1a;产品订单的数据分析与需求预测 23页论文及实现代码 相关链接 &#xff08;1&#xff09;建模方案 【2023年第十一届泰迪杯数据挖掘挑战赛】B题&#xff1a;产品订单的数据分析与需求预测 建模及python代码详解 问题一…

python为什么长期霸占榜首

Python霸占榜首 只因它真的很强 Python&#xff0c;年龄可能比很多读者都要大&#xff0c;但是它在更新快速的编程界却一直表现出色&#xff0c;甚至有人把它比作是编程界的《葵花宝典》&#xff0c;只是Python的速成之法相较《葵花宝典》有过之而无不及。 Python简洁&#x…

【Hello Network】UDP协议

作者&#xff1a;小萌新 专栏&#xff1a;网络 作者简介&#xff1a;大二学生 希望能和大家一起进步 本篇博客简介&#xff1a;简单介绍传输层和UDP协议 UDP协议 传输层端口号端口号范围划分知名端口号端口号与进程netstat与iostatpidof UDP协议UDP协议格式udp的数据封装udp的数…

2.2磁盘分区

2.2 磁盘分区 一个磁盘可以被划分为多个分区&#xff0c;有一个磁盘并且将他们划分为C;D;E盘&#xff0c;那个C,D,E就是分区&#xff08;partition&#xff09;。 2.2.1 磁盘连接的方式与设备文件名的关系 个人计算机常见的磁盘接口有两种&#xff0c;分别是SATA与SAS接口&…

精品:Stimulsoft Forms 2023.2.2

Stimulsoft Forms 是一种用于创建、编辑、填写、发布、分发交互式表单和收集结果的工具。我们的产品可嵌入到应用程序中&#xff0c;并允许您创建各种自定义填充模板。丰富的功能使模板具有真正的交互性。用户会收到 PDF 格式的可填写表格&#xff0c;他们可以在任何支持此格式…

ZooKeeper安装与配置集群

简介: ZooKeeper是一个分布式的&#xff0c;开放源码的分布式应用程序协调服务&#xff0c;是Google的Chubby一个开源的实现&#xff0c;是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件&#xff0c;提供的功能包括&#xff1a;配置维护、域名服务、分布…

ChatGPT初尝试——合并Excel表格【AI版】

ChatGPT真的能提升生产力吗&#xff1f;【AI版】 无意间的刷到的视频一、提需求二、ChatGPT用Python编写代码三、意外的解释四、ChatGPT改用Java编写代码五、GPT第二次生成Java代码尾巴 无意间的刷到的视频 晚上在家刷视频&#xff0c;突然看到一个在讲关于AI编程的视频&#…

网络安全:网络信息安全的概述.

网络安全&#xff1a;网络信息安全的概述 网络信息安全是一门涉及计算机科学&#xff0c;网络技术&#xff0c;通信技术、密码技术、信息安全技术、应用数学、数论、信息论等多种学科的综合性学科。 它主要是指网络系统的硬件、软件及其系统中的数据受到保护&#xff0c;不受…

深度学习 -- Pytorch学习 数据集API Dataset与DataLoader 重载鸢尾花数据集

前言 在模型训练的步骤中&#xff0c;数据的部分非常重要&#xff0c;它的过程主要分为数据收集、数据划分、数据读取、数据预处理。 数据收集的有原始样本和标签(Img&#xff0c;label) 数据集的划分需要分为训练集、验证集、测试集。 训练集负责训练模型&#xff0c;验证集…

C++基础知识快速入门

目录 一, 命名空间 1.1命名空间使用命名空间中成员该如何使用呢&#xff1f; 1.2 命名空间其他2种使用方式&#xff1a; 1. using 将其中一个成员引入 2. using namespace 二&#xff0c;C输入&输出 &#xff08;简单运用&#xff09; 2. 1 头文件 2. 2 cout 与 …

2023年软件测试常见面试题100%问必背全套教程

随着数字化时代的到来&#xff0c;软件测试越来越受到重视。在未来的几年里&#xff0c;软件测试将继续成为信息技术领域中的热门职业之一。如果你是一名正在寻找或准备进入软件测试行业的人&#xff0c;那么这套常见面试题全套教程对你来说会非常有用。 这套教程旨在帮助你了…

MySQL主从同步配置

Mysql 主从同步原理 就是基于数据库里面的那个 binlog 文件进行数据同步 具体步骤&#xff1a; step1&#xff1a;master将数据改变记录到二进制日志&#xff08;binary log&#xff09;中。step2&#xff1a; 当slave上执行 start slave 命令之后&#xff0c;slave会创建一个…

Unity 后处理(Post-Processing) -- (2)创建后处理配置文件

通过前面一小节&#xff0c;我们初步认识了后处理是什么&#xff0c;在Unity中简单的试了试后处理的效果。本节我们来创建一个我们自己的后处理配置文件&#xff08;post-processing profile&#xff09;。 一个后处理配置文件包含了一系列为了达到特定视觉效果的后处理效果的配…

C51 - DS18B20

Thermometer 1> 实验概述2> 硬件设计3> DS18B204> 单总线&#xff08;1-Wire&#xff09;通讯协议4.1> 复位时序4.2> 写-DS18B20时序4.3> 读-DS18B20时序4.4> 命令 5> 程序设计5.1> 编程思路5.2> 代码实现 6> 联调测试 1> 实验概述 实现…

Redis集合底层实现原理

目录 本章重点简单动态字符串SDS集合底层实现原理zipListlistPackskipListquickListKey 与Value中元素的数量 本章重点 掌握Redis简单动态字符串了解Redis集合底层实现原理 简单动态字符串SDS SDS简介 我们Redis中无论是key还是value其数据类型都是字符串.我们Redis中的字符…

从入门到精通:30天速成黑客教程到底有多狠?

首先我谈下对黑客&网络安全的认知&#xff0c;其实最重要的是兴趣热爱&#xff0c;不同于网络安全工程师&#xff0c;他们大都是培训机构培训出来的&#xff0c;具备的基本都是防御和白帽子技能&#xff0c;他们绝大多数的人看的是工资&#xff0c;他们是为了就业而学习&am…