【后端面经-Java】JVM垃圾回收机制
1. Where:回收哪里的东西?——JVM内存分配
JVM垃圾回收机制(Garbage Collect,简称GC)主要负责回收JVM内存当中未被及时释放回收的内存区域,JVM垃圾回收机制让程序员摆脱了手动释放内存的操作,降低了程序员疏忽大意导致的风险。
那么,垃圾回收机制到底针对哪一块的内存空间进行处理呢?是整体内存还是某一块内存?
在回答这个问题之前,我们需要先了解一下JVM内存分配机制,JVM内存分配机制主要有如下几个区域:
- 栈(Stack)
- 堆(Heap)
- 方法区(Method Area)
- 本地方法栈(Native Method Stack)
- 程序计数器(PC)
我们需要知道的是,栈、本地方法栈、程序计数器和方法调用有关,是线程私有的,随着方法结束,栈和本地方法栈的栈帧会出栈,释放内存,因此,这三部分并不需要使用垃圾回收机制。
而堆主要存放对象实例和数组,而方法区存放类的信息、编译信息、常量池等,这两块区域由于是线程共享的,在方法调用结束之后也不会被释放内存(毕竟A用完了,不知道B、C、D会不会用到这部分)需要使用垃圾回收机制进行内存回收。
关于每个区域的具体内容,可参考博客:【后端面经-Java】JVM内存分区详解
因此,内存回收机制主要针对堆和方法区进行处理。
2. Which:内存对象中谁会被回收?——GC分代思想
2.1 年轻代/老年代/永久代
JVM垃圾回收机制中,一般都是基于GC分代思想进行算法设计。
GC机制将内存内容分为三部分:
年轻代(Young Generation):新产生的实例基本上都处于这代,因为新产生的实例大部分都是一次性的,因此这部分内存需要经常进行内存回收;老年代(Old Generation):在新生代残酷频繁的筛选机制中,多次存活下来的实例会进入老年代,老年代意味着生命周期较长,一般在内存没有满之前不会对这部分内存进行回收;永久代(Permanent Generation,JCK1.8之后改成元空间(Metaspace)):永久代存放的是JVM程序运行相关的元数据,比如类信息、方法信息、常量池等,内容重要且占空间小,因此基本上不会进行垃圾回收。
如果要类比的话,年轻代就是刚刚步入职场的小青年,不稳定性较高,很容易被裁员(垃圾回收),而熬过这个阶段,成为技术骨干(老年代)之后,基本上不会在正常公司运行过程中被裁员,除非公司倒闭(内存已满),而永久代或者元空间就是公司最高层的管理人员,对公司的运行起着关键作用,一般情况下不会被裁员。
2.2 内存细分
- 堆和方法区
堆中存放年轻代和老年代,而方法区中存放永久代。 - 新生代区域细分
新生代区域又分为Eden区、Survivor0区和Survivor1区,比例为8:1:1。
整体内存细分情况如图所示:

3. When:什么时候回收垃圾?——GC触发条件
GC按照触发条件,可分为Scavenge GC和Full GC。
- Scavenge GC(Minor GC)
Scavenge GC是指年轻代Eden区的垃圾回收,触发条件为:- 年轻代Eden区域内存不足
- 调用Scavenge GC之后,将未清理的元素放入Survivor0区域。如果Survivor0区域内存不足,则将Survivor0区域内存中的所有元素放入Survivor1区域。清空Survivor0区域,然后将Survivor1中的元素放入Survivor0中;
- 如果Survivor1区域内存不足,则将Survivor1区域内存中的所有元素放入老年代中;
- 如果老年代也内存不足,则会考虑触发Full GC。
- 年轻代Eden区域内存不足
- Full GC(Major GC)
Full GC是整体对内存的垃圾回收,包括年轻代和老年代,触发条件为:- 老年代内存不足
- 永久代内存不足
- 显性调用
system.gc() - 上次GC之后堆内存的划分出现变化
对于GC线程,其本身的优先级比较低,因此在CPU空闲的时候,可能会进行GC处理,而在忙时基本上不会进行GC处理,除非此时内存空间不足,需要GC处理之后才能正常运行。
Full GC对于计算资源是一个很大的消耗,应该尽量避免使用Full GC。
4. Why:凭什么说它是垃圾?——垃圾判断算法
前面主要介绍了JVM垃圾回收机制的针对对象,GC分代思想和触发条件。那么,好好的一个对象实例,GC机制空口无凭凭什么说它是垃圾呢?这就需要垃圾判断算法了。
常见的垃圾判断方法有两种:引用计数法和可达性分析法。
4.1 引用计数法
- 每个对象实例都有一个引用计数器,当有一个引用指向该对象实例时,引用计数器加1,当引用失效时,引用计数器减1,当引用计数器为0时,该对象实例就是垃圾,需要进行回收。
- 补充一下JVM的引用类型,如下图所示:
- 优点:判断逻辑简单;
- 缺点:无法解决
循环引用的问题(从图论角度来说就是环状节点无法识别)。
4.2 可达性分析法
- 将每个实例看作节点,两个实例之间的引用关系看作路径。从GC Roots开始,对堆内存中的对象进行遍历,如果某个对象实例没有被遍历到,则说明该对象实例不可达,不可达则是垃圾,对其进行垃圾标记,等待后续回收(非连通图查找连通子图的数量)。
- GC Roots指的是正在运行的程序中一些基本对象,从这些对象往下查找其引用对象,包括如下几种:
虚拟机栈(栈帧中的本地变量表)中引用的对象方法区中类静态属性引用的对象方法区中常量引用的对象本地方法栈中JNI(Native方法)引用的对象
- 优点:能有效解决循环引用的问题;
- 缺点:判断逻辑复杂,需要遍历整个内存空间,效率较低。
5. How:如何对待垃圾?——垃圾回收算法
常见的垃圾回收算法包括:标记-清除算法、标记-整理算法、复制算法和分代收集算法(自适应算法)。
5.0 垃圾的垂死挣扎
在被处决之前,垃圾会进行一次垂死挣扎,实例第一次被标记为垃圾之后,如果可以进行一次有效finalize()方法调用,和其他实例建立引用,那么该实例就会被复活,不会被回收。
5.1 标记-清除算法
在垃圾判断算法执行完成后,已经被明确判断成垃圾的实例,清除法在原地释放其内存空间,将其标记为可用空间,等待后续的内存分配。
- 优点:操作简单,原地清除不需要复制内存
- 缺点:会产生内存碎片,长期运行会影响CPU运行效率
5.2 标记-整理算法
标记-整理算法在标记完成之后,将所有存活的实例移动到一端,然后清除掉另一端的内存空间,这样就可以有效解决内存碎片的问题。
- 优点:无内存碎片;
- 缺点:需要移动实例,效率较低;
5.3 复制算法
复制算法将内存空间分为两块,每次只使用其中一块,当一块内存空间内存满了之后,将存活的实例复制到另一块内存空间中,然后清除掉之前的内存空间。
- 优点:无内存碎片,操作简单;标记和复制可并行;
- 缺点:可用内存空间直接减半,内存利用率较低;
5.4 分代收集算法
针对不同代的数据特点,使用不同的垃圾回收算法。
- 年轻代:复制算法
- 存活对象较少,复制算法每次的对象复制不会有太大负担;
- 操作频繁,复制算法标记和复制可并发处理,效率较高;
- 老年代:标记-整理算法
- 存活对象较多,减少内存碎片,提高可用性
- 永久代(元空间):不作考虑
6. Who:谁去处理垃圾?——垃圾回收器
垃圾回收器是垃圾回收算法的执行者,常见的垃圾回收器如下图所示:

连线部分说明这两个垃圾回收器能够搭配使用。
6.1 年轻代-Serial收集器
- 回收算法:复制算法
- 单线程:执行回收算法的时候是单线程;适用于并发能力较低的系统。
- Stop the World:一般线程和回收线程无法并行,执行回收算法需要中断其他线程,这个现象称为Stop the World(乱入Dio的“咋瓦鲁多”)。
- Stop the World现象会导致系统暂停,引出
垃圾收集停顿时间这一参数,影响用户体验,因此需要尽量避免;
- Stop the World现象会导致系统暂停,引出
- 优点:简单高效,适用于单核CPU;
- 缺点:无法并行,且单线程处理效率较低;
- 启用方式:
-XX:+UseSerialGC
6.2 年轻代-ParNew收集器
- 回收算法:复制算法
- 多线程:执行回收算法的时候是多线程;
- 也会存在Stop the World现象,除了多线程的改进之外,和Serial收集器没有太大区别;
- 优点:多线程处理效率高,适用于多核CPU;
- 缺点:一般线程和回收线程无法并行处理;
- 启用方式:
-XX:+UseParNewGC
6.3 年轻代-Parallel Scavenge收集器
- 回收算法:复制算法
- 关注吞吐量
吞吐量 = 用户线程运行时间 / (用户线程运行时间 + 垃圾回收线程运行时间)
- 相关参数
- 垃圾收集停顿时间
- 设置方式:
-XX:MaxGCPauseMillis=一个数值 - 停顿时间过大将会直接影响每次垃圾回收的用户体验,停顿时间过小则会导致垃圾回收频繁;
- 吞吐量大小
- 设置方式:
-XX:GCTimeRatio=一个数值 - 默认取值为
99%,表示只有1%的时间用于垃圾回收;
- 自适应模式
- 设置方式:
-XX:+UseAdaptiveSizePolicy - 设置自适应模式之后,内存中的新生代分配比例和老年代的时间参数可自行调整,达到吞吐量、停顿时间和内存占用的平衡;
- 是JDK1.8的默认垃圾回收器
- 启用方式:
-XX:+UseParallelGC
6.4 老年代-SerialOld收集器
- 回收算法:标记-整理算法
- 单线程,可类比年轻代的Serial收集器,优缺点同理
- CMS收集器的后备算法
6.5 老年代-ParallelOld收集器
- 回收算法:标记-压缩算法
- 关注吞吐量,可类比年轻代的Parallel Scavenge收集器
- 多线程,线程数通过
-XX:ParallelGCThreads设置,默认为CPU核心数 - 启用方式:
-XX:+UseParallelOldGC
6.6 老年代-CMS(Concurrent Mark Sweep)收集器
- 回收算法:标记-清除算法
- 关注停顿时间,期望有较短的垃圾回收停顿时间,从而优化用户体验;
- 回收步骤:
- 初始标记:适用可达性分析法的思想,标记GC Roots能直接关联到的对象,速度较快;
- 并发标记:一般线程和回收线程并行,对此时标记状态出现变化的实例进行统计;
- 重新标记:根据并发标记的结果,对标记状态发生变化的实例进行重新标记,这一步相对较慢;
- 并发清除:清理垃圾实例,释放内存空间;这一步可以和一般线程并行;
- 相关参数:
- 触发阈值:
- 设置方式:
-XX:CMSInitiatingOccupancyFraction=一个数值 - 和之前讨论的何时进行垃圾回收的触发机制不同,CMS在处理老年代的时候,不会等到内存完全占满,而是会设置一个阈值,默认数值为
68%,占用内存空间超过这个阈值就进行垃圾回收处理。
- 设置方式:
- 整理标记
- 设置方式:
-XX:+UseCMSCompactAtFullCollection - 如果设置这一标记,则垃圾回收之后会进行一次整理,合并内存碎片。
- 设置方式:
- 触发阈值:
- 优点:并发收集,提高执行效率;减少停顿时间,用户体验佳
- 缺点:无法处理浮动垃圾,对CPU资源敏感,且标记清除算法会产生内存碎片
6.7 年轻代和老年代-G1(Garbage First)收集器
- 回收算法:整体来看是标记-整理算法,局部来看是复制算法
- 关注停顿时间,也关注高吞吐量(我全都要.jpg);
- 分区:不同于之前所讨论的分代划分内存区域的方式,G1回收器将内存划分为一个又一个单元区域,称为
Region,- 设置方式:
-XX:G1HeapRegionSize=一个数值 - 每个分区内部可以存放年轻代或者老年代的数据,根据不同的存放数据,将分区划分为四类:
E-Eden区:存放年轻代当中Eden区域的数据;S-Survivor区:存放年轻代当中Survivor区域(survivor0和survivor1)的数据;O-Old:存放老年代的一般数据;H-Humongous:存放老年代当中大对象的数据;当占据整个Region一半以上的时候,就会被划分为Humongous区域;
- 设置方式:
- 停顿预测模型:用户可以自行设置垃圾回收停顿时间,而G1回收器会根据历史数据构建预测模型,考虑为了满足用户设置的停顿时间,本次垃圾回收可以处理哪几个Region。
- Region优先级队列:G1浏览器维护一个Region队列,高价值的Region有更高的优先级,在垃圾回收的时候优先处理,这也是
Garbage First名字的由来。 - 回收步骤(和CMS回收器类似):
- 初始标记
- 并发标记
- 重新标记
- 并发清除
- 优点:并发操作,并行收集,提高执行效率;关注停顿时间,可预测停顿时间,优化用户体验;可处理浮动垃圾,不会产生内存碎片;
6.8 垃圾回收器对比图
对上述垃圾回收器的对比如下所示:

面试模拟
Q:介绍一下JVM和垃圾回收机制。
A:从"where"、"whice"、"when"、"why"、"how"、"who"的角度,重点介绍触发机制/判断算法/垃圾回收算法/垃圾回收机制。
参考资料
【后端面经-Java】JVM垃圾回收机制的更多相关文章
- java JVM垃圾回收机制
Java语言出来之前,大家都在拼命的写C或者C++的程序,而此时存在一个很大的矛盾,C++等语言创建对象要不断的去开辟空间,不用的时候有需要不断的去释放控件,既要写构造函数,又要写析构函数,很多时候都 ...
- Java虚拟机学习笔记——JVM垃圾回收机制
Java虚拟机学习笔记——JVM垃圾回收机制 Java垃圾回收基于虚拟机的自动内存管理机制,我们不需要为每一个对象进行释放内存,不容易发生内存泄漏和内存溢出问题. 但是自动内存管理机制不是万能药,我们 ...
- JVM系列(三):java的垃圾回收机制
java垃圾回收机制介绍 上一篇讲述了JVM的内存模型,了解了到了绝大部分的对象是分配在堆上面的,我们在编码的时候并没有显示的指明哪些对象需要回收,但是程序在运行的过程中是会一直创建对象的,之所 ...
- JVM内存管理和JVM垃圾回收机制
JVM内存管理和JVM垃圾回收机制(1) 这里向大家描述一下JVM学习笔记之JVM内存管理和JVM垃圾回收的概念,JVM内存结构由堆.栈.本地方法栈.方法区等部分组成,另外JVM分别对新生代和旧生代采 ...
- JVM基础系列第8讲:JVM 垃圾回收机制
在第 6 讲中我们说到 Java 虚拟机的内存结构,提到了这部分的规范其实是由<Java 虚拟机规范>指定的,每个 Java 虚拟机可能都有不同的实现.其实涉及到 Java 虚拟机的内存, ...
- 了解java中垃圾回收机制
Java的垃圾回收机制是Java环境自带有的,它不像c语言的malloc申请空间后需要Free()函数来释放,而Java中的代码块中所申请的空间可在程序执行完成后自动释放,但是是有局限性的,代码块所占 ...
- JVM内存管理、JVM垃圾回收机制、新生代、老年代以及永久代
内存模型 JVM运行时数据区由程序计数器.堆.虚拟机栈.本地方法栈.方法区部分组成,结构图如下所示. JVM内存结构由程序计数器.堆.栈.本地方法栈.方法区等部分组成,结构图如下所示: 1)程序计数器 ...
- JVM 垃圾回收机制和常见算法和 JVM 的内存结构和内存分配(面试题)
一.JVM 垃圾回收机制和常见算法 Sun 公司只定义了垃圾回收机制规则而不局限于其实现算法,因此不同厂商生产的虚拟机采用的算法也不尽相同.GC(Garbage Collector)在回收对象前首先必 ...
- 面试官,不要再问我“Java GC垃圾回收机制”了
Java GC垃圾回收几乎是面试必问的JVM问题之一,本篇文章带领大家了解Java GC的底层原理,图文并茂,突破学习及面试瓶颈. 楔子-JVM内存结构补充 在上篇<JVM之内存结构详解> ...
- 【Java学习笔记】Java的垃圾回收机制
搬以前写的博客[2014-12-30 15:07] 以前很少关注内存的问题,基本没有关注,这方面的小白,原因在于自己都是写的自我娱乐的小程序,不关注性能,不是提供服务.而企业级别的应用在程序稳健性方面 ...
随机推荐
- React onBlur回调中使用document.activeElement返回body解决方案
最开始想实现一个功能,点击img图标后给出购物下拉框CartDropdown,当img及CartDropdown失去焦点时隐藏CartDropdown. 最开始的核心代码如下: export defa ...
- 浏览器发送POST请求、DELETE请求
1.浏览器发送POST请求 方法一: var xml = new XMLHttpRequest(); var url = "http://127.0.0.1:8800/admin/user& ...
- iOS APP启动广告实现方式 与 APP唤端调用
APP启动广告功能实现要从2个方面思考 一是UI方案,怎样处理广告页与主页之间的切换方式. 二是广告页展示时机,是使用后台实时广告数据还是使用本地缓存广告数据.后台数据方式获取广告最新但是用户要等待后 ...
- springboot升级过程中踩坑定位分析记录 | 京东云技术团队
作者:京东零售 李文龙 1.背景 " 俗话说:为了修复一个小bug而引入了一个更大bug " 因所负责的系统使用的spring框架版本5.1.5.RELEASE在线上出过一个偶发的 ...
- 几行代码教你快速创建scrapy项目,非常实用建议收藏!
import shutil,os修改settings.py def config(scrapy_path,project_name): judge=input("是否自动修改配置?是:yes ...
- 快速上手Linux核心命令(十):Linux安装软件
目录 前言 rpm rpm包管理器 yum 自动化RPM包管理工具 前言 这期呢主要说一说Linux中包软件管理相关命令,这一期的命令虽然只有两个.但 软件包的安装和卸载都是我们平常最常用的,需要熟练 ...
- 使用Kepserver 自带 DataLogger 功能 实现工控数据转储关系型数据库
本文以 Mysql数据库为例,介绍使用 kepserver 的datalogger 功能转储数据到 mysql 第一步:下载安装 Mysql ODBC 数据库驱动前往 官网下载ODBC驱动https: ...
- 2022-05-13:k8s安装webrtc-streamer,yaml如何写?
2022-05-13:k8s安装webrtc-streamer,yaml如何写? 答案2022-05-13: yaml如下: apiVersion: apps/v1 kind: Deployment ...
- Windows常用的 CMD 命令合集
常用的 CMD 命令合集: 基础命令 dir:列出当前目录中的文件和子目录. cd:更改当前目录.例如,cd Documents 将当前目录更改为 Documents 文件夹. md 或 mkdir: ...
- # 代码随想录算法训练营Day31 贪心算法| 1005.K次取反后最大化的数组和 134. 加油站 135. 分发糖果
代码随想录算法训练营 1005.K次取反后最大化的数组和 题目链接:1005.K次取反后最大化的数组和 给定一个整数数组 A,我们只能用以下方法修改该数组:我们选择某个索引 i 并将 A[i] 替换为 ...