垃圾收集算法

2023-09-13 19:27:35

1.如何判断对象是否存活?

1.1引用计数算法

基本思路:

  • 在对象中添加一个引用计数器
  • 每当有一个地方引用它的时候,计数器就加+1
  • 每当有一个引用失效的时候,计数器就减-1
  • 当计数器的值为0的时候,那么该对象就是可被GC回收的垃圾对象

存在的问题:对象循环引用

a 对象引用了 b 对象,b 对象也引用了 a 对象,ab 对象却没有再被其他对象所引用了,其实正常来说这两个对象已经是垃圾了,因为没有其他对象在使用了,但是计数器内的数值却不是 0,所以引用计数算法就无法回收它们。

1.2. 可达性分析算法

又称为根可达性算法

基本思路:

过定义了一系列称为“GC Roots”的根对象作为起始节点集,从 GC Roots 开始,根据引用关系往下进行搜索,查找的路径我们把它称为 "引用链" 。当一个对象到 GC Roots之间没有任何引用链相连时(对象与GC Roots之间不可达),那么该对象就是可被GC回收的垃圾对象

可达性分析算法也是JVM 默认使用的寻找垃圾算法。

例如:

Object 6Object 7Object 8彼此之前有引用关系,但是没有与"GC Roots"相连,那么就会被当做垃圾所回收。

2.Java 中的四种引用类型

 无论上述哪种算法,我们都可以看到引用在其中的重要作用。

2.1.强引用(Strong Reference)

强引用是使用最普遍的引用。如果一个对象具有强引用,垃圾回收器绝不会回收它。当内存空间不足时,JVM 即使抛出OOM错误,使程序异常终止,也不会随意回收具有强引用对象来解决内存不足的问题。

Object strongReference = new Object();

如果强引用对象不使用时,需要弱化从而使GC能够回收。

弱化方式1

显式地设置strongReference对象为nullgc认为该对象不存在引用,这时就可以回收这个对象。

注意:具体什么时候收集取决于GC算法, 例如,strongReference全局变量时,就需要在不用这个对象时赋值为null,因为强引用不会被垃圾回收。

strongReference = null;

弱化方式2:

让对象超出作用域范围。在一个方法的内部有一个强引用,这个引用保存在VM Stack中(GC Root),而真正的引用对象(Object)保存在中。当这个方法运行完成后,就会退出方法栈,则这个对象会被回收。

public void test() {
    Object strongReference = new Object();
    // 省略其他操作
}

2.2. 软引用(Soft Reference)

如果一个对象只具有软引用,则内存空间充足时,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。所以,软引用可用来实现内存敏感的高速缓存。

// 软引用
String str = new String("abc");
SoftReference<String> softReference = new SoftReference<String>(str);

// 访问软引用
softReference.get();

 当内存不足时,JVM首先将软引用中的对象引用置为null,然后通知垃圾回收器进行回收:

public class Demo01 {
    public static void main(String[] args) {
        //强引用
        String strongReference = new String("abc");

        String str = new String("just do do");
        //弱引用,进行保护,内存充足不回收。
        SoftReference<String> softReference = new SoftReference<>(str);
        str = null;

        // Notify GC
        System.gc();

        try {
            byte[] buff1 = new byte[900000000]; // 内存充沛
//            byte[] buff2 = new byte[900000000];
//            byte[] buff3 = new byte[900000000];
//            byte[] buff4 = new byte[900000000]; // 内存不足
        } catch (Error e) {
            e.printStackTrace();
        }
        System.out.println(strongReference);
        System.out.println(softReference.get()); // just do do 或 null
    }
}

内存充足:

内存不足:

 上述案例可以看出:强引用与软引用在内存不足时的回收情况。

2.3. 弱引用(Weak Reference)

只具有弱引用的对象拥有更短暂生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。

创建弱引用,使用WeakReference

public static void main(String[] args) {
        String str = new String("abc");
        WeakReference<String> weakReference = new WeakReference<>(str);
        str = null;

        System.gc();

        // 一旦发生GC,弱引用一定会被回收
        System.out.println(weakReference.get());//结果为null
    }

2.4. 虚引用(Phantom Reference)

虚引用是最弱的一种引用关系,如果一个对象仅持有虚引用,完全不会对其生存时间构成影响,它就和没有任何引用一样,随时可能会被回收。

虚引用,主要用来跟踪对象被垃圾回收的活动,可以在垃圾收集时收到一个系统通知。

3.垃圾收集算法

2.1. 分代收集理论

目前主流JVM虚拟机中的垃圾收集器,都遵循分代收集理论:

  • 弱分代:绝大多数对象都是朝生夕灭
  • 强分代:经历越多次垃圾收集过程的对象,越难以回收,难以消亡

        按照分代收集理论设计的“分代垃圾收集器”,所采用的设计原则:收集器应该将Java堆划分成不同的区域,然后将回收对象依据其年龄(年龄即对象经历过垃圾收集过程的次数)分配到不同的区域存储。

2.2.分代存储

如果一个区域中大多数对象都是朝生夕灭(新生代),难以熬过垃圾收集过程的话,把它们集中存储在一起,每次回收时,只关注如何保留少量存活对象,而不是去标记大量将要回收的对象,就能以较低代价回收到大量的空间。

如果一个区域中大多数对象都是难以回收(老年代),那么把它们集中放在一起,JVM虚拟机就可以使用较低的频率,来对这个区域进行回收。

2.3.分代收集

垃圾收集类型划分:

  • 部分收集(Partial GC):没有完整收集整个Java堆的垃圾收集,其中又分为:
    • 新生代收集(Minor GC / Young GC
    • 老年代收集(Major GC / Old GC
    • 混合收集(Mixed GC):收集整个新生代和部分老年代的垃圾收集。
  • 整堆收集(Full GC):收集整个Java堆的垃圾收集。

2.4.具体垃圾收集算法

Java堆区划分成不同区域后,垃圾收集器才可以针对不同的区域,安排与该区域存储对象存亡特征相匹配的垃圾收集算法:标记-复制算法标记-清除算法标记-整理算法等。

2.4.1.标记-清除算法( Mark-Sweep )

实现思路:

        分为“标记”和“清除”阶段:首先标记出所有不需要回收的对象,在标记完成后统一回收掉所有没有被标记的对象。它是最基础的收集算法,后续的算法都是对其不足进行改进得到。

存在的问题:

  1. 执行效率不稳定问题:如果执行垃圾收集的区域,大部分对象是需要被回收的,则需要大量的标记和清除动作,导致效率变低。
  2. 内存空间碎片化问题:标记清除后会产生大量不连续的碎片,空间碎片太多,会导致分配较大对象时,无法找到足够的连续空间,从而会触发新的垃圾收集动作。

 黑色:可回收对象      蓝色:存活对象       白色:未使用空间

2.4.2.标记-复制算法 ( Copying )

        “标记-复制”收集算法简称“复制算法”,为了解决“标记-清除”面对大量可回收对象时执行效率低下的问题。

实现思路:
        该算法将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把已使用的空间一次清理掉。

存在的问题:

  1. 对象存活率较高,需要进行较多的内存间复制,效率降低
  2. 浪费过多的内存,使现有的可用空间变为原先的一半

2.4.3. 标记-整理算法 ( Mark-Compact )

实现思路:

        标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行回收,而是让所有存活的对象向内存空间一端移动, 然后直接清理边界以外的内存,这样清理的机制,不会像标记-清除那样留下大量的内存碎片。

综上所述:

        当前虚拟机的垃圾收集都基于分代收集思想,根据对象存活周期的不同,将内存分为几个不同的区域,在不同的区域选择使用合适的垃圾收集算法

例如:Heap 堆分为新生代老年代,这样我们就可以根据各个年代的特点,从存活几率和额外的空间中选择合适的垃圾收集算法,

更多推荐

充分利用学习平台,提升个人职业竞争力

在当今竞争激烈的职场环境中,个人职业竞争力的提升变得至关重要。而充分利用学习平台成为了我们提升竞争力的一种有效途径。学习平台不仅可以提供丰富多样的学习资源,还能提升个人技能和知识水平,让我们更具竞争力。学习平台提供了丰富的学习资源,为我们进一步学习和提升提供了便利。无论是在线课程、教学视频、学习资料还是学术期刊,学习平

OSCP系列靶场-Esay-Gaara保姆级

OSCP系列靶场-Esay-Gaara目录OSCP系列靶场-Esay-Gaara总结准备工作信息收集-端口扫描目标开放端口收集目标端口对应服务探测信息收集-端口测试22-SSH端口的信息收集22-SSH端口版本信息与MSF利用22-SSH协议支持的登录方式22-SSH弱口令爆破(待定)22-SSH手动登录尝试(无)80

C 初级语法学习笔记(基础)

C程序结构,主要分为以下部分:1.预处理器指令#include用来引入头文件#define预先定义2.函数(标识符&关键字&运算符)存储类每个C语言程序都需要包含main()函数,main()函数是程序运行的入口。return_typefunction_name(parameterlist){bodyofthefunc

CS224W - Colab 5 学习笔记

Inthisnotebook,wewillfirstlearnhowtotransformNetworkXgraphsintoDeepSNAPrepresentations.Then,wewilldivedeeperintohowDeepSNAPstoresandrepresentsheterogeneousgraph

类加载器 - 双亲委派模型

文章目录回顾一下类加载过程类加载器类加载器介绍类加载器加载规则类加载器总结自定义类加载器双亲委派模型双亲委派模型介绍双亲委派模型的执行流程双亲委派模型的好处打破双亲委派模型方法本文参考:类加载器详解(重点)|JavaGuide(Java面试+学习指南)回顾一下类加载过程开始介绍类加载器和双亲委派模型之前,简单回顾一下类

怎样提高服务器安全性?45.248.10.x

服务器被攻击一般有两种比较常见的方式:一是恶意的攻击行为,如拒绝服务攻击,网络病毒等等,这种方式就是消耗服务器资源,影响服务器的正常运作,甚至服务器所在网络的瘫痪;另外一个就是恶意的入侵行为,这种行为更是会导致服务器敏感信息泄露,入侵者更是可以为所欲为,肆意破坏服务器。所以我们要保证网络服务器的安全可以说就是尽量减少网

vite + vue3 的项目中使用 vitest 做单元测试(仅供参考)

一、配置文件//vitest.config.tsimport{fileURLToPath}from'node:url'import{mergeConfig,defineConfig}from'vite'import{configDefaults}from'vitest/config'//importviteConfig

探索工业路由器如何助力无人驾驶方案的突破性解析

随着无人驾驶技术的发展,越来越多的企业和组织开始部署无人驾驶车辆来提高运输效率和安全性。在这些方案中,工业路由器被广泛应用于建立稳定、安全和高效的通信网络。在本篇文章中,我们将分享一个真实的无人驾驶方案部署案例,其中工业路由器使用了星创易联的SR800-d,它如何助力无人驾驶方案的实现。案例背景:某物流公司是一家专注于

【Node.js】模块化:

文章目录1、模块化的基本概念2、Node.js中模块化【1】Node.js中模块的分类【2】加载模块【3】模块作用域【4】向外共享模块作用域中的成员【5】模块化规范3、npm与包(包/依赖/插件)【1】包的基本知识:【2】开发属于自己的包【3】发布包4、模块的加载机制【1】优先从缓存中加载【2】内置模块的加载机制【3】

在Ubuntu系统上实现免费电脑IP更改

大家好!今天我要为大家分享一个关于如何在Ubuntu系统上免费实现电脑IP更改的知识。IP地址更改在某些情况下非常有用,可以帮助我们解决网络连接问题、绕过限制以及加强网络安全等。而在Ubuntu系统上,我们可以采用一些简单的方法来免费更改电脑的IP地址。现在让我们一起来了解一下吧!方法一:使用命令行工具Ubuntu系统

打开泰坦陨落提示msvcp120.dll丢失怎么办?三个解决方法快速解决

首先,我们来了解一下msvcr120.dll是什么文件。msvcr120.dll是一个动态链接库文件,它是MicrosoftVisualC++2013Redistributable中的一个组件。这个文件对于一些软件的运行是非常重要的,如果缺失或损坏,就会导致软件无法正常运行。那么,msvcr120.dll为什么会丢失呢

热文推荐