面试-JAVA垃圾回收

关于Java中JVM的介绍请查看另一篇博客:JVM介绍

垃圾回收

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

1、垃圾的确定

如何确定哪些是垃圾需要回收

1.1 引用计数法

在Java中,引用和对象是有关联的。如果要操作对象则必须用引用进行。因此,很显然一个简单的办法是通过引用计数来判断一个对象是否可以回收。简单说,即一个对象如果没有任何与之关联的引用,即他们的引用计数都不为0,则说明对象不太可能再被用到,那么这个对象就是可回收对象。(不能解决循环引用问题)

1.2 可达性分析算法

为了解决引用计数法的循环引用问题,Java使用了可达性分析的方法。通过一系列的“GC Roots"对象作为起点搜索。如果在“GC Roots”和一个对象之间没有可达路径,则称该对象是不可达的。

那么什么对象可以作为GC Roots?

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

在可达性分析法中不可达的对象,它们暂时处于“缓刑阶段”,要真正宣告一个对象死亡,至少要经历两次标记过程;可达性分析法中不可达的对象被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行 finalize 方法。当对象没有覆盖 finalize 方法,或 finalize 方法已经被虚拟机调用过时,虚拟机将这两种情况视为没有必要执行。被判定为需要执行的对象将会被放在一个队列中进行第二次标记,除非这个对象与引用链上的任何一个对象建立关联,否则就会被真的回收。

2、怎么回收

在Java中存在着四种垃圾回收算法,标记清除算法、复制算法、标记整理算法以及分代回收算法。

2.1 标记清除算法

该算法分为“标记”和“清除”两个阶段:

  • 标记阶段的任务是标记出所有需要被回收的对象
  • 清除阶段就是回收被标记的对象所占用的空间。

它是最基础的收集算法,效率也很高,但是会带来两个明显的问题:

  • 效率问题
  • 空间问题(标记清除后会产生大量不连续的碎片)

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

2.2 复制算法

为了解决效率问题,出现复制算法。它可以将内存分为大小相同的两块,每次使用其中的一块。当第一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。

简单来说就是该对象分为对象面以及空闲面,对象在对象面上创建,对象面上存活的对象会被复制到空闲面,接下来就可以清除对象面的内存。

这种算法的优缺点也比较明显

  • 优点:解决碎片化问题,顺序分配内存简单高效
  • 缺点:只适用于存活率低的场景,如果极端情况下如果对象面上的对象全部存活,就要浪费一半的存储空间。

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

2.3 标记整理算法

为了解决复制算法的缺陷,充分利用内存空间,提出了标记整理算法。该算法标记阶段和标记清除一样,但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存

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

2.4分代收集算法

当前虚拟机的垃圾收集都采用分代收集算法,这种算法就是根据具体的情况选择具体的垃圾回收算法。

一般将 java 堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。

在新生代中,每次收集都会有大量对象死去,所以可以选择复制算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。

老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择“标记-清除”或“标记-整理”算法进行垃圾收集

在java中,对象的内存在哪个时刻回收,取决于垃圾回收器何时运行

一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize()方法, 并且在下一次垃圾回收动作发生时,才会真正的回收对象占用的内存

3、垃圾回收器

Java 堆内存被划分为新生代和年老代两部分,新生代主要使用复制垃圾回收算法;年老代主要使用标记-整理垃圾回收算法,因此java 虚拟中针对新生代和年老代分别提供了多种不同的垃圾收集器,JDK1.6中Sun HotSpot 虚拟机的垃圾收集器如下:

在这里插入图片描述

3.1 Serial 垃圾回收器(单线程、复制算法)

Serial(英文连续)是最基本垃圾收集器,使用复制算法,曾经是JDK1.3.1之前新生代唯一的垃圾收集器。

Serial是一个单线程的收集器,它不但只会使用一个CPU或一条线程去完成垃圾收集工作,并且在进行垃圾收集的同时,必须暂停其他所有的工作线程,直到垃圾收集结束

Serial垃圾收集器虽然在收集垃圾过程中需要暂停所有其他的工作线程,但是它简单高效,对于限定单个CPU环境来说,没有线程交互的开销,可以获得最高的单线程垃圾收集效率,

因此 Serial垃圾收集器依然是java虚拟机运行在Client模式下默认的新生代垃圾收集器。

3.2 ParNew垃圾收集器(Serial+多线程)

ParNew垃圾收集器其实是Serial 收集器的多线程版本,也使用复制算法,除了使用多线程进行垃圾收集之外,其余的行为和Serial 收集器完全一样,ParNew垃圾收集器在垃圾收集过程中同样也要暂停所有其他的工作线程。

ParNew 收集器默认开启和CPU数目相同的线程数,可以通过-XX:ParallelGCThreads参数来限制垃圾收集器的线程数。【Parallel:平行的】

ParNew虽然是除了多线程外和Serial 收集器几乎完全一样,但是ParNew垃圾收集器是很多java虚拟机运行在Server模式下新生代的默认垃圾收集器。

3.3 Parallel Scavenge收集器(多线程复制算法、高效)

Parallel Scavenge 收集器也是一个新生代垃圾收集器,同样使用复制算法,也是一个多线程的垃圾收集器。

它重点关注的是程序达到一个可控制的吞吐量(Thoughput,CPU用于运行用户代码的时间/CPU总消耗时间,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)),高吞吐量可以最高效率地利用CPU时间,尽快地完成程序的运算任务,主要适用于在后台运算而不需要太多交互的任务。自适应调节策略也是ParallelScavenge 收集器与ParNew收集器的一个重要区别。

3.4 Serial Old收集器(单线程标记整理算法)

Serial Old是Serial垃圾收集器年老代版本,它同样是个单线程的收集器,使用标记-整理算法.

这个收集器也主要是运行在Client默认的java 虚拟机默认的年老代垃圾收集器。

在Server模式下,主要有两个用途:

1.在JDK1.5之前版本中与新生代的Parallel Scavenge收集器搭配使用。

2. 作为年老代中使用CMS收集器的后备垃圾收集方案。

新生代 Serial 与年老代Serial Old 搭配垃圾收集过程图:

在这里插入图片描述

3.5 Parallel Old收集器(多线程标记整理算法)

Parallel Old收集器是Parallel Scavenge的年老代版本,使用多线程的标记-整理算法,在JDK1.6才开始提供。

在JDK1.6之前,新生代使用ParallelScavenge 收集器只能搭配年老代的Serial Old 收集器,只能保证新生代的吞吐量优先,无法保证整体的吞吐量,Parallel Old正是为了在年老代同样提供吞吐量优先的垃圾收集器,如果系统对吞吐量要求比较高,可以优先考虑新生代 Parallel Scavenge和年老代Parallel Old 收集器的搭配策略。

新生代Parallel Scavenge和年老代Parallel Old 收集器搭配运行过程图:

在这里插入图片描述

3.6 CMS收集器(多线程标记清除算法)

Concurrent mark sweep(CMS)收集器是一种年老代垃圾收集器,其最主要目标是获取最短垃圾回收停顿时间,和其他年老代使用标记-整理算法不同,它使用多线程的标记-清除算法

最短的垃圾收集停顿时间可以为交互比较高的程序提高用户体验。

CMS工作机制相比其他的垃圾收集器来说更复杂,整个过程分为以下4个阶段:

(一)初始标记

只是标记一下GC Roots能直接关联的对象,速度很快,仍然需要暂停所有的工作线程

(二)并发标记

进行GC Roots 跟踪的过程,和用户线程一起工作,不需要暂停工作线程。

(三)重新标记

为了修正在并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,仍然需要暂停所有的工作线程。

(四)并发清除

清除GC Roots 不可达对象,和用户线程一起工作,不需要暂停工作线程。由于耗时最长的并发标记和并发清除过程中,垃圾收集线程可以和用户现在一起并发工作,所以总体上来看CMS收集器的内存回收和用户线程是一起并发地执行。

CMS收集器工作过程:
在这里插入图片描述

3.7 G1收集器(标记整理算法)

Garbage first垃圾收集器是目前垃圾收集器理论发展的最前沿成果,相比与CMS收集器,G1收集器两个最突出的改进是:

  • 基于标记-整理算法,不产生内存碎片。
  • 可以非常精确控制停顿时间,在不牺牲吞吐量前提下,实现低停顿垃圾回收。

G1收集器避免全区域垃圾收集,它把堆内存划分为大小固定的几个独立区域,并且跟踪这些区域的垃圾收集进度,同时在后台维护一个优先级列表,每次根据所允许的收集时间,优先回收垃圾最多的区域。

区域划分和优先级区域回收机制,确保G1收集器可以在有限时间获得最高的垃圾收集效率。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一些与 Java 垃圾回收相关的面试题及答案: 1. 什么是 Java垃圾回收机制? Java垃圾回收机制是指在 Java 应用程序运行过程中,当一些对象不再被引用,也就是不再被程序使用时,JVM 会自动回收这些对象所占用的内存空间,从而保证程序的正常运行。 2. 为什么需要垃圾回收垃圾回收的主要目的是帮助程序员减少手动内存管理的负担。在使用垃圾回收机制后,程序员不需要再手动释放内存,而是由 JVM 自动回收不再使用的对象。这可以提高开发效率,减少内存泄漏和内存溢出的风险。 3. 如何手动触发垃圾回收? 可以通过调用 System.gc() 方法手动触发垃圾回收。但是,这个方法只是向 JVM 发送一个垃圾回收请求,JVM 不一定会立即回收内存,因为垃圾回收是一个比较耗费资源的操作,JVM 会根据当前内存的使用情况和垃圾回收策略来决定是否回收内存。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [java垃圾回收机制及其面试题](https://blog.csdn.net/lonely_baby/article/details/129133683)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值