JVM篇3 —《GC垃圾回收机制》

本文详细介绍了Java的垃圾收集机制,包括GC概述、对象存活判断(引用计数法和可达性分析法)、四大引用类型以及方法区的回收。深入探讨了各种垃圾收集算法,如标记清除、标记复制、标记整理和三色标记算法。此外,文章还讨论了不同代的垃圾收集器,如Serial、ParNew、CMS和G1,分析了它们的优缺点和适用场景。
摘要由CSDN通过智能技术生成

一、GC概述

垃圾收集 Garbage Collection 通常被称为“GC”

jvm 中,程序计数器、虚拟机栈、本地方法栈都是随线程而生随线程而灭,栈帧随着方法的进入和退出做入栈和出栈操作,实现了自动的内存清理,因此,我们的内存垃圾回收主要集中于 java 堆和方法区中,在程序运行期间,这部分内存的分配和使用都是动态的.

1、GC回收判定
	引用计数法
	可达性分析法
2、GC回收算法
	标记清除算法 
	标记复制算法
	标记整理算法 
	分代回收算法
	复制算法
3、GC收集器
	串行收集器:
		Serial(young)、Serial Old(old)
	并行收集器:
    	ParNew(young)、Parallel Scavenge(young)、Parallel Old(old)
    并发收集器:	
	    CMS(old)、G1收集器	

二、判断对象是否存活

JVM空间不够就需要Garbage Collection了,一般共享区的都要被回收比如堆区以及方法区。

image-20210309131113082

在进行内存回收之前要做的事情就是判断那些对象是死的,哪些是活的

常用方法有两种 引用计数法可达性分析

1、引用计数法

思路:

给 Java 对象添加一个引用计数器,每当有一个地方引用它时,计数器 +1;
引用失效则 -1,当计数器不为 0 时,判断该对象存活;否则判断为死亡(计数器 = 0)。

优点:

实现简单,判断高效。

缺点:

无法解决 对象间 相互循环引用 的问题

code:

class GcObject {
   
    public Object instance = null;
}
public class GcDemo {
   
    public static void main(String[] args) {
   
        GcObject object1 = new GcObject(); // step 1 
        GcObject object2 = new GcObject(); // step 2
        
        object1.instance = object2 ;//step 3
        object2.instance = object1; //step 4
        
        object1 = null; //step 5
        object2 = null; // step 6
    }
}
step1: GcObject实例1的引用计数+1,实例1引用数 = 1
step2: GcObject实例2的引用计数+1,实例2引用数 = 1
step3: GcObject实例2的引用计数+1,实例2引用数 = 2
step4: GcObject实例1的引用计数+1,实例1引用数 = 2
step5: GcObject实例1的引用计数-1,结果为 1
step6: GcObject实例2的引用计数-1,结果为 1

如上分析发现实例1跟实例2的引用数都不为0而又相互引用,这两个实例所占有的内存则无法释放。

2、可达性分析法

很多主流商用语言(如Java、C#)都采用引用链法判断对象是否存活,

大致的思路就是将一系列的 GC Roots 对象作为起点,从这些起点开始向下搜索。

在Java语言中,可作为 GC Roots 的对象包含以下几种:

  1. 第一种是虚拟机栈中的引用的对象,在程序中正常创建一个对象,对象会在堆上开辟一块空间,同时会将这块空间的地址作为引用保存到虚拟机栈中,如果对象生命周期结束了,那么引用就会从虚拟机栈中出栈,因此如果在虚拟机栈中有引用,就说明这个对象还是有用的,这种情况是最常见的。
  2. 第二种是我们在类中定义了全局的静态的对象,也就是使用了static关键字,由于虚拟机栈是线程私有的,所以这种对象的引用会保存在共有的方法区中,显然将方法区中的静态引用作为GC Roots是必须的。
  3. 第三种便是常量引用,就是使用了static final关键字,由于这种引用初始化之后不会修改,所以方法区常量池里的引用的对象也应该作为GC Roots。
  4. 第四种是在使用JNI技术时,有时候单纯的Java代码并不能满足我们的需求,我们可能需要在Java中调用C或C++的代码,因此会使用Native方法,JVM内存中专门有一块本地方法栈,用来保存这些对象的引用,所以本地方法栈中引用的对象也会被作为GC Roots。

GC Root步骤主要包含如下三步:

1、可达性分析:

image-20210309092132710

当一个对象到 GC Roots 没有任何引用链相连时,则判断该对象不可达。
注意: 可达性分析仅仅只是判断对象是否可达,但还不足以判断对象是否存活 / 死亡。

2、第一次标记 & 筛选

筛选的条件对象 如果没有重写finalize或者调用过finalize 则将该对象加入到F-Queue中

finalize()

类似 C++ 的析构函数,用于关闭外部资源。但是 try-finally 等方式可以做得更好,并且该方法运行代价很高,不确定性大,无法保证各个对象的调用顺序,因此最好不要使用。

当一个对象可被回收时,如果需要执行该对象的 finalize() 方法,那么就有可能在该方法中让对象重新被引用,从而实现自救。自救只能进行一次,如果回收的对象之前调用了 finalize() 方法自救,后面回收时不会再调用该方法。

3、第二次标记 & 筛选

当对象经过了第一次的标记 & 筛选,会被进行第二次标记 & 准备被进行筛选。

经过F-Queue筛选后如果对象还没有跟GC Root建立引用关系则被回收,属于给个二次机会。

image-20210309092425668

Java 虚拟机使用该算法来判断对象是否可被回收,在 Java 中 GC Roots 一般包含以下内容:

​ 虚拟机栈中引用的对象

​ 本地方法栈中引用的对象

​ 方法区中类静态属性引用的对象

​ 方法区中的常量引用的对象

3、四大引用类型

无论是通过引用计数算法判断对象的引用数量,还是通过可达性分析算法判断对象是否可达,判定对象是否可被回收都与引用有关。

Java 提供了四种强度不同的引用类型。

1、强引用

强引用(StrongReference)是使用最普遍的引用。

垃圾回收器绝对不会回收它,内存不足时宁愿抛出OOM导致程序异常,平常的new 对象就是。

使用 new 一个新对象的方式来创建强引用。

Object obj = new Object()
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值