jvm——垃圾回收机制(GC)详解

news2024/11/27 0:39:58

开始之前有几个GC的基本问题

  • 什么是GC?

    GC 是 garbage collection 的缩写,意思是垃圾回收——把内存(特别是堆内存)中不再使用的空间释放掉;清理不再使用的对象。

  • 为什么要GC?

    堆内存是各个线程共享的空间,不能无节制的使用。服务器运行的时间通常都很长。累积的对象也会非常多。这些对象如果不做任何清理,任由它们数量不断累加,内存很快就会耗尽。所以GC就是要把不使用的对象都清理掉,把内存空间空出来,让项目可以持续运行下去。

  • 什么样的对象是垃圾对象?

    不再使用或获取不到的对象是垃圾对象。

  • 如何把垃圾对象找出来?

    办法1:引用计数法(不采用,不能解决循环引用问题)

    办法2:可达性分析(从GC Roots对象出发,不可达的对象就是要清理的对象)

  • 找到垃圾对象如何执行清理?

    具体的GC算法

为什么要有垃圾回收?

  • 线程私有空间:无需由系统来执行GC。因为线程结束,释放自己刚才使用的空间即可,不影响其它线程。
  • 线程共享空间:任何一个线程结束时,都无法确定刚才使用的空间是不是还有别的线程在使用。所以不能因为线程结束而释放空间,必须在系统层面统一垃圾回收。

GC的基本原则:

  • 频繁收集新生代
  • 较少收集老年代
  • 基本不动元空间

如何确定垃圾

想要回收垃圾,必须得先知道,哪些对象可以被认定为垃圾。关于垃圾确定方式,主要有两种,分别是引用计数法可达性分析法,其原理分别如下:

引用计数法(不采用)

工作机制

  • 引用计数法是在对象每一次被引用时,都给这个对象专属的『引用计数器』+1。
  • 当前引用被取消时,就给这个『引用计数器』-1。
  • 当前『引用计数器』为零时,表示这个对象不再被引用了,需要让GC回收。
  • 可是当对象之间存在交叉引用的时候,对象即使处于应该被回收的状态,也没法让『引用计数器』归零。
        Member member01 = new Member();
        Member member02 = new Member();

images

        member01.setFriend(member02);
        member02.setFriend(member01);

images

        member01 = null;
        member02 = null;

images

引用计数法的关键问题:该清理的对象清理不掉

简单的说就是,在 Java 中,引用与对象相关联,如果要操作对象,则必须使用引用。因此,可以通过引用计数来确定对象是否可以回收。实现原则是,如果一个对象被引用一次,计数器 +1,反之亦然。当计数器为 0 时,该对象不被引用,则该对象被视为垃圾,并且可以被 GC 回收利用。

SpringMVC 组件

  • IOC 容器对象的接口类型:WebApplicationContext
    • WebApplicationContext 对象初始化过程中:将它自己存入 ServletContext 域
    • WebApplicationContext 对象也会把 ServletContext 存入 IOC 容器
  • Servlet 上下文对象:ServletContext

可达性分析法

核心原理:判断一个对象,是否存在从『堆外』到『堆内』的引用。

为了解决引用计数法的循环引用问题,Java 采用了可达性分析的方法。其实现原理是,将一系列"GCroot"对象作为搜索起点。如果在"GCroot"和一个对象之间没有可达的路径,则该对象被认为是不可访问的。

要注意的是,不可达对象不等价于可回收对象,不可达对象变为可回收对象至少要经过两次标记过程。两次标记后仍然是可回收对象,则将面临回收。

垃圾回收算法

了解了垃圾的确定方法后,我们将继续了解垃圾是怎么被回收的,即垃圾回收算法。在Java中主要有四中垃圾回收算法,分别是标记清除算法复制算法标记整理算法分代收集算法

基本算法:引用计数法(不推荐)

引用计数算法很简单,它实际上是通过在对象头中分配一个空间来保存该对象被引用的次数。如果该对象被其它对象引用,则它的引用计数加一,如果删除对该对象的引用,那么它的引用计数就减一,当该对象的引用计数为0时,那么该对象就会被回收。

引用计数垃圾收集机制,它只是在引用计数变化为0时即刻发生,而且只针对某一个对象以及它所依赖的其它对象。所以,我们一般也称呼引用计数垃圾收集为直接的垃圾收集机制。垃圾收集的开销被分摊到整个应用程序的运行当中了,而不是在进行垃圾收集时,要挂起整个应用的运行,直到对堆中所有对象的处理都结束。因此,采用引用计数的垃圾收集不属于严格意义上的"Stop-The-World"的垃圾收集机制。

优点:

  • 实时性较高,不需要等到内存不够时才回收
  • 垃圾回收时不用挂起整个程序,不影响程序正常运行

缺点:

  • 回收时不移动对象, 所以会造成内存碎片问题
  • 不能解决对象间的循环引用问题

标记清除算法

“标记-清除”算法是最基础的算法,它的做法是当堆中的有效内存空间被耗尽的时候,就会暂停、挂起整个程序(也被称为stop the world),然后进行两项工作,第一项则是标记,第二项则是清除。

  • 标记:标记的过程其实就是,从根对象开始遍历所有的对象,然后将所有存活的对象标记为可达的对象。

  • 清除:清除的过程将遍历堆中所有的对象,将没有标记的对象全部清除掉。

  • 优点:实现简单

  • 缺点:

    • 效率低,因为标记和清除两个动作都要遍历所有的对象
    • 垃圾收集后有可能会造成大量的内存碎片
    • 垃圾回收时会造成应用程序暂停

标记清除算法.png

由标记清除算法的实现我们可以看出,其主要存在两个缺点:

  • 效率问题。标记和清除过程的效率都不高
  • 空间问题。标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致当程序在以后的运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作

 复制算法

为了解决标记清除算法内存碎片化严重的缺陷,提出了复制算法。复制算法主要思想是,按内存容量将内存划分为大小相等的两块区域。每次只使用其中一块,当这一块内存满后将其中存活的对象复制到另一块上去,然后把该内存中的垃圾对象清理掉,其实现过程如图:

复制算法.png

  • 优点1:在垃圾多的情况下(新生代),效率较高

  • 优点2:清理后,内存无碎片

  • 缺点:浪费了一半的内存空间,在存活对象较多的情况下(老年代),效率较差

复制算法虽然实现简单,内存效率高,不易产生碎片,但是最大的问题是可用内存被压缩到了原本的一半。且存活对象增多的话,Copying 算法的效率会大大降低

标记整理算法

结合了以上两个算法,为了避免缺陷而提出。标记阶段和标记清理算法相同,标记后不是清理对象,而是将存活对象移向内存的一端。然后清除端边界外的对象。如图:

标记整理算法.png

分代收集算法

在结合以上三种算法的综合分析及 JVM 内存对象生命周期的特点,诞生了一种新的垃圾回收算法——分代收集算法。其核心思想是根据对象存活的不同生命周期将内存划分为不同的域,一般情况下将 GC 堆划分为老年代(Tenured/Old Generation)和新生代(Young Generation)。老年代的特点是每次垃圾回收时只有少量对象需要被回收,新生代的特点是每次垃圾回收时都有大量垃圾需要被回收,因此可以根据不同区域选择不同的算法。

分代算法其实就是这样的,根据回收对象的特点进行选择。

  • 新生代适合使用复制算法
  • 老年代适合使用标记清除或标记压缩算法

垃圾回收器

串行垃圾回收器

串行:在一个线程内执行垃圾回收操作。

  • 新生代串行回收器 SerialGC:采用复制算法实现,单线程垃圾回收,独占式垃圾回收器
  • 老年代串行回收器 SerialOldGC:采用标记压缩算法,单线程独占式垃圾回收器

并行垃圾回收器

并行:在多个线程中执行垃圾回收操作。

  • 新生代 ParNew 回收器:采用复制算法实现,多线程回收器,独占式垃圾回收器。
  • 新生代 ParallelScavengeGC 回收器:采用复制算法多线程独占式回收器
  • 老年代 ParallelOldGC 回收器: 采用标记压缩算法,多线程独占式回收器
  1. CMS回收器

    CMS全称 (Concurrent Mark Sweep),是一款并发的、使用标记-清除算法的垃圾回收器。对CPU资源非常敏感。

    启用CMS回收器参数 :-XX:+UseConcMarkSweepGC。

    使用场景:GC过程短暂停顿,适合对时延要求较高的服务,用户线程不允许长时间的停顿。

    优点:最短回收停顿时间为目标的收集器。并发收集,低停顿。

    缺点:服务长时间运行,造成严重的内存碎片化。算法实现比较复杂。

  • G1回收器

    G1(Garbage-First)是一款面向服务端应用的并发垃圾回收器, 主要目标用于配备多颗CPU的服务器,治理大内存。是JDK1.7提供的一个新收集器,是当今收集器技术发展的最前沿成果之一。

    G1计划是并发标记-清除收集器的长期替代品。

    启用G1收集器参数:-XX:+UseG1GC启用G1收集器。

    G1将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了, 它们都是一部分Region(不需要连续)的集合。

    images

    每块区域既有可能属于Old区、也有可能是Young区,因此不需要一次就对整个老年代/新生代回收。而是当线程并发寻找可回收的对象时,有些区块包含可回收的对象要比其他区块多很多。虽然在清理这些区块时G1仍然需要暂停应用线程,但可以用相对较少的时间优先回收垃圾较多的Region(这也是G1命名的来源)。这种方式保证了G1可以在有限的时间内获取尽可能高的收集效率。

    特点:

    • 一整块堆内存被分成多个独立的区域Regions

    • 存活对象被拷贝到新的Survivor区

    • 新生代内存由一组不连续的堆heap区组成,使得可以动态调整各个区域

    • 多线程并发GC

    • young GC会有STW(Stop the world)事件

垃圾回收器对比

新生代回收器

名称串行/并行/并发回收算法适用场景可以与CMS配合
SerialGC串行复制单CPU
ParNewGC并行复制多CPU
ParallelScavengeGC并行复制多CPU且关注吞吐量

老年代回收器

名称串行/并行/并发回收算法适用场景
SerialOldGC串行标记压缩单CPU
ParNewOldGC并行标记压缩多CPU
CMS并发,几乎不会暂停用户线程标记清除多CPU且与用户线程共存

finalize 机制

总体机制介绍

java.lang.Object 类中有一个方法:

protected void finalize() throws Throwable { }

方法体内是空的,说明如果子类不重写这个方法,那么不执行任何逻辑。

  • 在执行 GC 操作前,调用 finalize() 方法的是 Finalizer 线程,这个线程优先级很低。
  • 在对象的整个生命周期过程中,finalize() 方法只会被调用一次。

代码验证

public class FinalizeTest {

    // 静态变量
    public static FinalizeTest testObj;

    @Override
    protected void finalize() throws Throwable {
        // 重写 finalize() 方法
        System.out.println(Thread.currentThread().getName() + " is working");

        // 给待回收的对象(this)重新建立引用
        testObj = this;
    }

    public static void main(String[] args) {

        // 1、创建 FinalizeTest 对象
        FinalizeTest testObj = new FinalizeTest();

        // 2、取消引用
        testObj = null;

        // 3、执行 GC 操作
        System.gc();

        // ※ 让主线程等待一会儿,以便调用 finalize() 的线程能够执行
        try { TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {}

        // 4、判断待回收的对象是否存在
        if (FinalizeTest.testObj == null) {
            System.out.println("待回收的对象没有获救,还是要被 GC 清理");
        } else {
            System.out.println("待回收的对象被成功解救");
        }

        // 5、再次取消引用
        FinalizeTest.testObj = null;

        // 6、再次执行 GC 操作
        System.gc();

        // 7、判断待回收的对象是否存在
        if (FinalizeTest.testObj == null) {
            System.out.println("待回收的对象没有获救,还是要被 GC 清理");
        } else {
            System.out.println("待回收的对象被成功解救");
        }
    }

}

执行效果:

Finalizer is working
待回收的对象被成功解救
待回收的对象没有获救,还是要被 GC 清理

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/867413.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

spring常用注解标签总结

1:Component等 名称Component/Controller/Service/Repository类型类注解位置类定义上方作用设置该类为spring管理的bean属性value(默认):定义bean的id 说明: Component注解如果不起名称,会有一个默认值就是当前类名首…

IDEA提示:StringBuffer xxx‘ may be declared as ‘StringBuilde

如图所示,编写代码时遇见了如下IDEA警告: 原因:StringBuilder是线程不安全的,但是其效率高,而StringBuffer则相反,虽然其线程安全,但是效率低下。 由于 StringBuilder 相较于 StringBuffer 有速…

Java+Excel+POI+testNG基于数据驱动做一个简单的接口测试【杭州多测师_王sir】

一、创建一个apicases.xlsx放入到eclipse的resource里面&#xff0c;然后refresh刷新一下 二、在pom.xml文件中加入poi和testng的mvn repository、然后在eclipse的对应目录下放入features和plugins&#xff0c;重启eclipse就可以看到testNG了 <!--poi excel解析 --><d…

音视频研发分享:关键帧截图+wasm快照--我又做了一件有益于社会的事情

音视频研发分享&#xff1a;关键帧截图wasm快照--我又做了一件有益于社会的事情 简单的一个视频设备快照功能到底有多费事多费电&#xff1f;新的方法有方法&#xff01; 省了多少电&#xff1f; 简单的一个视频设备快照功能到底有多费事多费电&#xff1f; 以前&#xff0c;我…

【C# 基础精讲】构造函数和析构函数

构造函数&#xff08;Constructor&#xff09;和析构函数&#xff08;Destructor&#xff09;是面向对象编程中的两个重要概念&#xff0c;它们分别用于在对象创建和销毁的时候执行特定的操作。这两个函数在C#以及其他面向对象编程语言中都具有重要的作用&#xff0c;用于初始化…

机器学习笔记:李宏毅chatgpt 大模型 大资料

1 大模型 1.1 大模型的顿悟时刻 Emergent Abilities of Large Language Models&#xff0c;Transactions on Machine Learning Research 2022 模型的效果不是随着模型参数量变多而慢慢变好&#xff0c;而是在某一个瞬间&#xff0c;模型“顿悟”了 这边举的一个例子是&#…

剪映:制作特效的常用方法

在创作短视频时&#xff0c;常常需要为一些镜头添加或制作特效&#xff0c;以增加趣味性、提升影片的艺术渲染力。本文介绍几种在剪映专业版中快速添加或制作特效的常用方法。 一、使用特效库 在“特效”库中提供了大量的特效供下载使用。找到自己中意的特效&#xff0c;直接拖…

alphassl便宜通配符SSL证书推荐

AlphaSSL是一家提供SSL证书的CA认证机构&#xff0c;其证书可以保护网站的安全性&#xff0c;防止黑客攻击和信息泄露。AlphaSSL的证书价格实惠&#xff0c;安全性高&#xff0c;AlphaSSL的证书还可以与各种服务器和网站平台兼容&#xff0c;包括Apache、IIS、Tomcat和Nginx等。…

【C# 基础精讲】类和对象的概念

在面向对象编程&#xff08;Object-Oriented Programming&#xff0c;OOP&#xff09;中&#xff0c;类和对象是两个核心概念&#xff0c;用于描述和实现现实世界中的实体和关系。OOP 是一种编程范式&#xff0c;通过将数据和操作封装为对象来组织和管理代码&#xff0c;使得代…

大模型时代,如何重塑AI人才的培养?知名高校专家为您解答

当下&#xff0c;随着人工智能技术的快速发展&#xff0c;大模型已经成为了人工智能发展的新方向&#xff0c;同时也对新时代AI人才的需求和培养带来了新的思考与挑战&#xff0c;需要结合当下社会对复合型AI人才的需求进行新思考&#xff0c;创新AI人才培养模式&#xff0c;以…

基于frida检测demo来学习frida检测及anti

原文地址:https://www.zhuoyue360.com/crack/108.html 前言 随着逆向的攻防强度不断的提升,目前主流的移动安全厂商的加固服务基本上都已包含了常见Hook框架的反调试,我们最常见的hook工具如下: fridaxposed 为了更好的提升自己相关的经验,我们可以拿这类demo来进行原理的学…

如何限制运行时间的一则考虑

在日常使用中&#xff0c;会遇到限制过期时间的问题&#xff0c;但是&#xff0c;对于时间的判断&#xff0c;很难找到一个信任根&#xff01;如果没有信任根&#xff0c;这个问题其实无从判断。 从实用的角度来设计&#xff0c;我们假定可以找到一个相对可信的信任根&#xf…

从零实战SLAM-第三课(李群与李代数)

在七月算法报的班&#xff0c;老师讲的蛮好。好记性不如烂笔头&#xff0c;关键内容还是记录一下吧&#xff0c;课程入口&#xff0c;感兴趣的同学可以学习一下。 --------------------------------------------------------------------------------------------------------…

2023年中国负极石墨用坩埚市场规模现状及前景分析:负极材料为行业增长助推器[图]

负极石墨用坩埚分为再生坩埚和石墨匣钵&#xff0c;其中&#xff0c;再生坩埚主要应用于艾奇逊炉工艺的石墨化工序&#xff0c;石墨匣钵主要应用于预碳化和碳化工序。 负极石墨用坩埚分类 资料来源&#xff1a;共研产业咨询&#xff08;共研网&#xff09; 得益于动力电池的旺…

Git Cherry-pick使用

概述 无论项目大小&#xff0c;当你和一群程序员一起工作时&#xff0c;处理多个 Git 分支之间的变更都会变得很困难。有时&#xff0c;与其把整个 Git 分支合并到另一个分支&#xff0c;不如选择并移动几个特定的提交。这个过程被称为 "挑拣", 即 Cherry-pick。 本…

java获取到heapdump文件后,如何快速分析?

简介 在之前的OOM问题复盘之后&#xff0c;本周&#xff0c;又一Java服务出现了内存问题&#xff0c;这次问题不严重&#xff0c;只会触发堆内存占用高报警&#xff0c;没有触发OOM&#xff0c;但好在之前的复盘中总结了dump脚本&#xff0c;会在堆占用高时自动执行jstack与jm…

百度云盘发展历程与影响

摘要&#xff1a; 百度云盘作为中国领先的云存储与共享服务提供商&#xff0c;自其创立至今经历了多个阶段的发展与变革。本论文通过对百度云盘的历史回顾与分析&#xff0c;探讨了其在技术、商业模式、用户体验以及对社会的影响等方面的演变。同时&#xff0c;还分析了在竞争激…

谈谈Java开发语言

目录 1.概念 2.特点 3.应用领域 4.就业情况 1.概念 Java是一种面向对象的编程语言&#xff0c;它由James Gosling和他的团队在1995年于Sun Microsystems&#xff08;现在是Oracle Corporation&#xff09;开发出来。Java的设计目标是让开发者能够编写一次代码&#xff0c;在…

服务器安装Tomcat

下载Tomcat 下载地址在这&#xff1a; Tomcat官网 下载完成以后把压缩包上传到服务器中&#xff08;我传到了www/java&#xff09;,进行解压(解压到)&#xff0c;如果没有进行指定解压到哪里&#xff0c;默认是到root文件夹中 tar -zxvf /www/java/apache-tomcat-9.0.103.tar.…

策略模式实战应用

场景 假设做了个卖课网站&#xff0c;会员等级分为月vip、年vip、终生vip&#xff0c;每个等级买课的优惠力度不一样&#xff0c;传统的写法肯定是一堆的 if-else&#xff0c;现在使用策略模式写出代码实现 代码实现 策略模式的核心思想就是对扩展开放&#xff0c;对修改关闭…