JVM GC系列 — GC算法
一.前言
从本篇文章开始,将开始一个新的系列JVM。JVM是一个非常庞大且复制的技术体系,但是对于程序猿的升级,走向更高阶所必要经历的,曾经也下决心要好好学习一番,然而毅力不足都中途放弃。
GC的作用就是回收垃圾,但是要做到做点必须要解决两个问题:
- 如何确定哪些是垃圾
- 怎样回收垃圾
这两个问题可谓是GC的核心,本篇文章将从算法角度学习GC是怎样解决这两个问题。
### 二.如何确定哪些是垃圾
1.引用计数法
在Java应用中,可被回收的对象必然是无用对象,即没有其他对象引用它或者其脱离了应用的中的对象整体:
如上图,蓝色标记的对象之间相互引用,都是存活对象,GC不应该回收。而红色标记的对象,已经没有任何对象引用它,GC应该回收它。
从以上可以确定,GC首先应该回收没有任何引用指向其的对象。如何表达没有任何引用?
为每个对象维护一个计数器,该计数器表示指向其的引用。当没有引用其时,则计数器应该为0,GC则应该回收。这种算法被称为引用计数法。
如上图,当计数器为0时,表示该对象已经没有任何引用,可被垃圾收集器回收。该算法实现非常简单,且性能较佳,但是存在局限性。
2.引用计数法的局限性
引用计数法固然简单易于理解,但是它无法解决相互引用的问题。当两个以上的对象之间相互引用,但是它们已经脱离整个Java对象的整体时,引用计数法便无法表达出它们是可回收对象:
如上图,当两个红色的标记的对象相互引用,此外没有任何对象引用它们,显然它们是可回收对象。但是引用计数法会造成无法被回收。
假如有以下代码:
void referenceCount() {
...
A a = new A();
B b = new B(a);
a.setB(b);
...
}
A和B之间相互引用,但是此外没有任何对象引用A和B,那么当线程运行退出referenceCount方法时,A和B依然不能被回收。
3.可达性分析算法
由于引用计数法的局限性,显然它不适合作为JVM中定位可回收对象的算法。实际上再JVM中使用另一种方式表示对象是否可回收 — 可达性分析算法。
可达性分析算法:能从GC Roots找到一条到该对象的引用链路,则该对象就是存活对象,如何找不到,则该对象就是可回收对象。
这里首先要明白什么是GC Roots:
- 栈(栈帧中的本地变量表)中引用的对象
- 方法区中的静态成员
- 方法区中的常量引用的对象
- 本地方法栈中JNI(一般说的Native方法)引用的对象
Note:
简而言之,可达性分析算法就是根搜索算法,类似于树结构的搜索。其中GC Roots就是根,其他对象就是树上的节点。在根和节点之间寻找路径。
从图中可以看出,可达性分析算法能够解决相互引用的问题。从GC Roots开始到对象之间存在一条引用链路,则就是存活对象,反之即是死亡对象。
### 三.怎样回收垃圾
通过以上的可达性分析算法,GC能够找到堆区中的可回收对象,但是具体怎样回收才能具有更高的效率呢?在JVM中存在以下几种回收算法:
- 标记清除
- 复制
- 标记整理
以上三种算法都各有利弊,下面逐一介绍。
1.标记清除算法
顾名思义,该算法分为两个阶段,先标记出可回收对象,然后再清除这些对象。
将堆区看成以上连输的方块片段,红色代表没有GC Roots引用的对象,蓝色代表有引用。
标记清除算法,首先针对堆区对象进行标记,标记出没有GC Root是对象引用的对象。然后再将没有GC Roots引用的对象清除。
该算法的效率较高,只需要标记然后清除即可。但是从图中也可以看出问题所在,在清除后,会造成大量的不连续的内存碎片,当有大对象分配时,将又会触发GC,从而影响性能。
2.复制算法
为了解决标记清除算法的导致的内存碎片问题,复制算法因此而生。复制算法的思想是将内存分为两块,每次只使用其中一块,回收时将存活的对象复制到另一块中,然后将使用的那块完全清除,再使用新的复制那块。
经过复制算法后,不会再产生断断续续的内存碎片。每次垃圾回收之后,都能得到连续的大片内存。但是又产生了新的问题,内存是昂贵资源,将其分成两块,每次只使用其中一块,造成了资源浪费。但是实际中,对象基本上都是朝生夕死,能存活的对象少之又少,所以实际中一般不会按照1:1的比例分块。在Hostspot的年轻代默认按照8:1的进行划分,即Eden:Survivor=8:1。
3.标记整理算法
为了既解决标记清除算法带来的内存碎片问题,又能很好解决复制算法的内存牺牲问题。又出现标记整理算法。它类似标记清除,但是又增加了一个整理过程。即将存活对象往一端移动,整理内存碎片,形成连续内存。
通过标记清理,能够将回收垃圾对象,释放内存。通过整理压缩,能够形成连续内存,解决内存碎片。
虽然标记整理算法解决了复制算法和标记清除算法带来的问题,但是整理压缩也是耗费性能,降低效率。
4.分代算法
以上的GC回收算法都各有利弊,实际使用中,根据内存区域的划分以及其特点,HotSpot不单一采用以上的算法,而是根据堆区不同分代,分别采用以上算法,这种算法被称为分代算法。
由于Java对象具有朝生夕死的特点,堆区一般划分为新生代(年轻代)和老年代(年老代)。新生代对象生存周期短暂,老年代对象生成周期较长:
- 新生代对象存活周期短,每次只有少量对象存活,使用复制算法比较适宜。所以HotSpot中将新生代分为Eden和Survivor区域。
- 老年代对象存活时间长,每次GC后,大部分对象都存活。所以使用标记整理算法。
### 总结
本篇文章主要介绍GC中两个核心问题,第一:GC如何确定哪些对象是可回收的,在HotSpot中使用从GC Roots开始的可达性分析算法,定位对象是否存活。第二:在确定对象的存活与否后,采用何种策略回收对象。商业虚拟机中多数采用分代算法解决该问题。在了解了GC回收算法后,下篇文章再围绕这些算法学习HotSpot中的有哪些具体实现。
JVM GC系列 — GC算法的更多相关文章
- JVM GC系列 — GC收集器
一.前言 前文学习了各种GC回收算法,掌握了GC回收的原理,但是真正的GC实现却尤为复杂,本篇文章将主要介绍各种GC收集器. 目前主流的HotSpot VM支持多种虚拟机,这些虚拟机也体现了GC的发展 ...
- JVM内存管理------GC算法精解(五分钟教你终极算法---分代搜集算法)
引言 何为终极算法? 其实就是现在的JVM采用的算法,并非真正的终极.说不定若干年以后,还会有新的终极算法,而且几乎是一定会有,因为LZ相信高人们的能力. 那么分代搜集算法是怎么处理GC的呢? 对象分 ...
- JVM学习之GC常用算法
出处:博客园左潇龙的技术博客--http://www.cnblogs.com/zuoxiaolong,多谢分享 GC策略解决了哪些问题? 既然是要进行自动GC,那必然会有相应的策略,而这些策略解决了哪 ...
- JVM基础系列第14讲:JVM参数之GC日志配置
说到 Java 虚拟机,不得不提的就是 Java 虚拟机的 GC(Garbage Collection)日志.而对于 GC 日志,我们不仅要学会看懂,而且要学会如何设置对应的 GC 日志参数.今天就让 ...
- JVM中的GC算法,JVM参数,垃圾收集器分类
一.在JVM中什么是垃圾?如何判断一个对象是否可被回收?哪些对象可以作为GC Roots的根 垃圾就是在内存中已经不再被使用到的空间就是垃圾. 1.引用计数法: 内部使用一个计数器,当有对象被引用+1 ...
- 【JVM第八篇--垃圾回收】GC和GC算法
写在前面的话:本文是在观看尚硅谷JVM教程后,整理的学习笔记.其观看地址如下:尚硅谷2020最新版宋红康JVM教程 1.垃圾 1.1.什么是垃圾 垃圾(Garbage)在Java语言中是指在运行程序中 ...
- JVM学习笔记——GC算法
GC 算法 GC 即 Garbage Collection 垃圾回收.JVM 中的 GC 99%发生在堆中,而 Java 堆中采用的垃圾回收机制为分代收集算法.即将堆分为新生代和老年代,根据不同的区域 ...
- JVM组成、GC回收机制、算法、JVM常见启动参数、JAVA出现OOM,如何解决、tomcat优化方法
JVM组成.GC回收机制.算法.JVM常见启动参数.JAVA出现OOM,如何解决.tomcat优化方法
- JVM架构和GC垃圾回收机制
深入理解系列之JDK8下JVM虚拟机(1)——JVM内存组成 https://blog.csdn.net/u011552404/article/details/80306316 JVM架构和GC垃圾回 ...
随机推荐
- Java程序远程无法执行nohup命令
问题的上下文: 由于生产无法使用 jenkins 发布,所以采用 ch.ethz.ssh2 或叫 ganymed-ssh2 的开源 java 的 ssh api 进行远程发布. 在发起重启时,远程执行 ...
- [译]Vulkan教程(19)渲染和呈现
[译]Vulkan教程(19)渲染和呈现 Rendering and presentation 渲染和呈现 Setup 设置 This is the chapter where everything ...
- [译]Vulkan教程(10)交换链
[译]Vulkan教程(10)交换链 Vulkan does not have the concept of a "default framebuffer", hence it r ...
- Swoole 启动一个服务,开启了哪些进程和线程?
目录 概述 代码 小结 概述 Swoole 启动一个服务,开启了哪些进程和线程? 为了解决这个问题,咱们启动一个最简单的服务,一起看看究竟启动了哪些进程和线程? 然后结合官网运行流程图,对每个进程和线 ...
- java将前端的json数组字符串转换为列表
记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表. 前端数据转化与请求 var contracts = [ {id: '1', name: 'yanggb合同1'}, {i ...
- java高并发系列【共34篇,强力建议观看】
第1天:必须知道的几个概念 第2天:并发级别 第3天:有关并行的两个重要定律 第4天:JMM相关的一些概念 第5天:深入理解进程和线程 第6天:线程的基本操作 第7天:volatile与Java内存模 ...
- Asp.Net Core 内置IOC容器的理解
Asp.Net Core 内置IOC容器的理解 01.使用IOC容器的好处 对接口和实现类由原来的零散式管理,到现在的集中式管理. 对类和接口之间的关系,有多种注入模式(构造函数注入.属性注入等). ...
- PlayJava Day013
今日所学: /* 2019.08.19开始学习,此为补档. */ 1.BufferedImage:是Image的一个子类,两者的主要作用就是将一副图片加载到内存中,即图像缓冲区. 对于本地图片: Fi ...
- Java生鲜电商平台-交易对账以及跟商家对账的思考
Java生鲜电商平台-交易对账以及跟商家对账的思考 说明:对于任何一家电商而言,资金的安全尤为重要,在资金管理过程中,涉及到交易订单的对账以及商家的对账,那i么如何来保证对账的高效与准确呢? 公司在搭 ...
- 25个JavaScript数组方法代码示例
摘要: 通过代码掌握数组方法. 原文:通过实现25个数组方法来理解及高效使用数组方法(长文,建议收藏) 译者:前端小智 Fundebug经授权转载,版权归原作者所有. 要在给定数组上使用方法,只需要通 ...