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垃圾回 ...
随机推荐
- [C]副作用和序列点
概述 副作用: <C语言核心技术>对副作用的描述: 表达式内包含了一串的常量.标识符.运算符(指示的运算方式).表达式的目的可以是获得结果值,或者得到运算的副作用(side effect) ...
- Jmeter脚本录制攻略
基于Apache JMeter(5.2.1) 首先在TestPlan里添加一个HTTP代理服务武器: 设置端口,在目标控制器里选择线程组. 在Chrome浏览器里设置代理: 点击启动按钮后,在浏览器登 ...
- java调用py文件传参执行
java执行的也是py文件主函数. 传参: String url="*********************"; String[] args1 = new String[] { ...
- java之instanceof操作符
a intanceof A:判断a是否是类A的的一个实例,返回值为boolean public class Person extends Object{} public class Student e ...
- libwebrtc & libmediasoupclient编译
本文简单介绍在Ubuntu下libwebrtc的编译过程. 由于网速限制,实际编译过程是在远程vps上编译滴. 系统环境 Ubuntu 18.04系统的虚拟主机. root@vultr:~# pwd ...
- PHP 将远程文件写入到pdf或者word
/** * 下载 */public function download($ids = null){ //一些条件参数啥的 $data = []; //获取文件 $res = curl_post(url ...
- mysql分布式
一,复制,对数据进行备份,实现搞可用,提高吞吐量,实现高性能. 1,主从架构 2,多主架构 3,主主从从 4,主备 (实际用得多) 二,分片/分库分表 () 1,垂直拆分 1,垂直分表 2,垂直分库 ...
- Centos 7 下安装 Jenkins
Jenkins介绍 Jenkins是一个开源的支持自动化构建.部署等任务的平台.基本上可以说是持续集成(CI).持续发布(CD)不可或缺的工具. 安装Java环境 CentOS 7 安装 JAVA环境 ...
- Java 添加Word文本水印、图片水印
水印是一种常用于各种文档的声明.防伪手段,一般可设置文字水印或者加载图片作为水印.以下内容将分享通过Java编程给Word文档添加水印效果的方法,即 文本水印 图片水印 使用工具:Free Spire ...
- Java并发总结
Java并发 进程 进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的.系统运行一个程序即是一个进程从创建,运行到消亡的过程. 在 Java 中,当我们启动 main 函数时其实就是 ...