JVM学习-垃圾回收(一)

news2025/3/14 20:46:25
什么是垃圾
  • 垃圾是指在运行程序中没有任何指针指向的对象,这个对象就是需要被回收的垃圾
  • 如果不及时对内存的垃圾进行清理,垃圾对象所占用的内存空间会一直保留到应用程序结束,被保留的空间无法被其它对象所用,甚至可能导致内存溢出
为什么需要GC
  • 对于高级语言来说,一个基本认知是如果不进行垃圾回收,内存迟早会被消耗完,因为不断地分配内存空间而不进行回收,就好像不停地生产生活垃圾而从来不打扫一样
  • 除了释放没用的对象,垃圾回收也可以清理内存里的记录碎片,碎片整理将所占用的堆内存移到堆的一端,以便JVM整理出的内存分配给新的对象
  • 随着应用程序所应付的业务越来越庞大、复杂,用户越来越多,没有GC就不能保证应用程序的正常进行。而经常造成STW的GC又跟不上实际的需求,所以才会不断地尝试对GC进行优化
早期垃圾回收
  • 早期C/C++时代,垃圾回收基本上是手工进行的,开发人员使用new申请内存,使用delete关键字进行内存释放
  • 这种方式可以灵活控制内存释放的时间,但给开发人员带来频繁申请和释放内存的管理负担,倘若一处内存区间由于程序员编码的问题忘记被回收,那么就会产生内存泄漏,垃圾对象永远无法清理,随着系统运行时间不断增长,垃圾对象所耗内存持续上升,直到出现内存溢出并造成应用程序崩溃
Java回收机制
  • 优势
    • 自动内存管理,无需开发人员手动参与内存的分配与回收,这样降低内存泄漏和内存溢出的风险
    • 自动内存管理机制,将程序员从繁重的内存管理中释放出来,可以更专心地专注于业务开发
  • 担忧
    • 对于Java开发人员,自动内存管理就像一个黑匣子,如果过度依赖于“自动”,那么这将会是一场灾难,最严重会弱化Java开发人员在程序出现内存溢出定位问题和解决问题的能力。
    • 了解JVM的自动内存分配和内存回收原理显得非常重要,只有在真正了解JVM是如何管理内存后,才能够遇到OutOfMemoryError时,快速地根据错误异常日志定位问题和解决问题
    • 当需要排查各种内存溢出、内存泄漏问题时,当垃圾收集成为系统达到更高并发量的瓶颈时,我们必须对“自动化”技术实施必要的监控和调节
  • 垃圾回收器可以对年轻代回收,也可以对老年代回收,甚至是全堆和方法区进行回收
    • Java堆是垃圾回收器的工作重点
  • 从次数上讲
    • 频繁收集Young区
    • 较少收集Old区
    • 基本不动Perm区(元空间)
垃圾回收算法
标记阶段
  • 在堆里存放着几乎所有的Java对象实例,在GC执行垃圾回收之前,首先需要区分出内存中哪些是存活对象,哪些是已经死亡的对象,只有被标记为已经死亡的对象,GC才会在执行垃圾回收时,释放掉其所占用的内存空间,这个过程称为垃圾标记阶段
  • 在JVM中如何标记一个死亡对象呢?简单的说,当一个对象已经不再被任何存活对象继续引用时,就可以审判已经死亡
  • 判断对象存活有两种方式
    • 引用计数算法(Reference Counting)
      • 较简单,对每个对象保存一个整型的引用计数器属性,用于记录对象被引用的情况
      • 对于一个对象A,只要有任何一个对象引用了A,则A的引用计数器就加1,当引用失效时,引用计数器就减1,只要对象A的引用计数器的值为0,即表示对象A不可能再被使用,可进行回收
      • 优点:实现简单,垃圾对象便于辨识,判定效率高,回收没有延迟性
      • 缺点
        • 它需要单独的字段存储计数器,这样增加了存储空间的开销
        • 每次赋值都需要更新计数器,伴随着加法和减法操作,增加时间开销
        • 引用计数器有一个严重的问题,即无法处理循环引用的情况,这是一条致命缺陷,导致在Java的垃圾回收器中没有使用此算法
          循环引用
    • python使用了引用计数算法,如何解决循环引用
      • 手动解除:在合适时机,解除引用关系
      • 使用弱引用weakref,weakref是python提供的标准库,旨在解决循环引用
//Java不是使用引用计数算法
public class ReferenceCountGC {
    private byte[] bigSize = new byte[5 * 1024 * 1024];
    Object reference = null;

    public static void main(String[] args) {
        ReferenceCountGC obj1 = new ReferenceCountGC();
        ReferenceCountGC obj2 = new ReferenceCountGC();

        obj1.reference = obj2;
        obj2.reference = obj1;

        obj1 = null;
        obj2 = null;
        //显示执行垃圾回收
        System.gc();
    }
}
//执行时添加参数-XX:+PrintGCDetails,不显示执行System.gc(),结果如下
Heap
 PSYoungGen      total 152576K, used 20726K [0x0000000716300000, 0x0000000720d00000, 0x00000007c0000000)
  eden space 131072K, 15% used [0x0000000716300000,0x000000071773d8d8,0x000000071e300000)
  from space 21504K, 0% used [0x000000071f800000,0x000000071f800000,0x0000000720d00000)
  to   space 21504K, 0% used [0x000000071e300000,0x000000071e300000,0x000000071f800000)
 ParOldGen       total 348160K, used 0K [0x00000005c2800000, 0x00000005d7c00000, 0x0000000716300000)
  object space 348160K, 0% used [0x00000005c2800000,0x00000005c2800000,0x00000005d7c00000)
 Metaspace       used 3425K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 380K, capacity 388K, committed 512K, reserved 1048576K
//执行时添加参数-XX:+PrintGCDetails,显示执行System.gc(),结果如下,
[GC (System.gc()) [PSYoungGen: 18104K->808K(152576K)] 18104K->816K(500736K), 0.0021479 secs] [Times: user=0.00 sys=0.00, real=0.02 secs] 
[Full GC (System.gc()) [PSYoungGen: 808K->0K(152576K)] [ParOldGen: 8K->645K(348160K)] 816K->645K(500736K), [Metaspace: 3476K->3476K(1056768K)], 0.0034007 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 PSYoungGen      total 152576K, used 3932K [0x0000000716300000, 0x0000000720d00000, 0x00000007c0000000)
  eden space 131072K, 3% used [0x0000000716300000,0x00000007166d7230,0x000000071e300000)
  from space 21504K, 0% used [0x000000071e300000,0x000000071e300000,0x000000071f800000)
  to   space 21504K, 0% used [0x000000071f800000,0x000000071f800000,0x0000000720d00000)
 ParOldGen       total 348160K, used 645K [0x00000005c2800000, 0x00000005d7c00000, 0x0000000716300000)
  object space 348160K, 0% used [0x00000005c2800000,0x00000005c28a1460,0x00000005d7c00000)
 Metaspace       used 3491K, capacity 4498K, committed 4864K, reserved 1056768K
  class space    used 387K, capacity 390K, committed 512K, reserved 1048576K

  • 可达性分析算法(根探索算法、追踪性垃圾收集)
    • 相对于引用计数算法而言,可达性分析算法不仅同样具备实现简单和执行高效,更重要的是该算法有效地解决在引用计数算法中循环引用的问题,防止内存泄漏的发生。
    • 相较于引用计数算法,可达性分析算法是Java、C#选择的,这种类型的垃圾收集通常叫做追踪性垃圾收集(Tracing Garbage Collection)
    • "GC Roots"根集合是一组必须活跃的引用
    • 基本思路
    • 可达性分析算法是以根对象集合为起始点,按照从上至下的方式搜索被根对象集体所连接的目标对象是否可达
    • 使用可达性分析算法后,内存中的存活对象都地被根对象集合直接或间接连接着,搜索所走过的路径称为引用链(Reference Chain)
    • 如果目标对象没有任何引用链相连,则是不可达的,意味着该对象已经死亡,可以标记为垃圾对象
    • 在可达性分析算法中,只能被根对象集合直接或间接连接的对象才是存活对象
      在这里插入图片描述
GC Roots
  • 虚拟机栈中引用的对象
    • 各个线程被调用的方法中使用到的参数、局部变量等
  • 本地方法内JNI引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
    • 字符串常量池(string table)里的引用
  • 所有被同步锁sychnorized持有的对象
  • Java虚拟机内部的引用
    • 基本数据类型对应的class对象,一些常驻的异常对象(NullPointerException,OutOfMemoryError),系统类加载器
  • 反映Java虚拟机内部情况的JMXBean,JVMTI中注册的回调、本地代码缓存等
    小技巧由于Root采用栈方式存放变量和指针,所以如果一个指针,它保存了堆内存里面的对象,但自己又不存放在堆内存里面,它就是一个Root
    **注:**如果要用可达性分析算法来羊汤内存是否回收,那么分析工作必须在一个能保障一致性的快照中进行,这点不满足的话,分析结果的准确性就无法保证,这也是导致GC进行时必须“Stop The World”的一个重要原因,即使用号称不会发生停顿的CMS收集器,枚举根节点时也是必须停顿的
对象finalizaion机制
  • Java语言提供了对象终止机制来允许开发人员提供对象被销毁之前的自定义处理逻辑
  • 当垃圾回收器发现没有引用指向一个对象,即:垃圾回收此对象之前,总会先调用这个对象的finalize()方法
  • finalize()方法允许在子类中被重写,用于在对象被回收时进行资源释放。通常在这个方法中进行一些资源释放和清理工作,比如关闭文件,套接字和数据库连接等
  • 永远不要主动调用finalize()方法
    • 在finalize()时可能会导致对象复活
    • finalize()方法的执行时间没有保障,完全由GC线程决定,极端情况下,若不发生GC,则finalize()方法将没有执行机会
    • 一个糟糕的finalize()地严重影响GC性能
  • 从功能上来说,finalize()方法与C++的析构函数比较相似,但是Java采用的是基于垃圾回收器的自动内存管理机制,所以finalize()方法在本质上不同于C++的析构函数
  • 由于finalize()方法存在,虚拟机中的对象一般有三种可能状态
    • 可触及的:从根节点开始,可以到达这个对象
    • 可复活的:对象的所有引用都被释放,但对象有可能在finalize()中复活
    • 不可触及的:对象的finalize()被调用,并且没有复活,那么就会进入不可触及状态,不可触及的对象不可能被复活,因为finalize()只会被调用一次
      注:只有对象不可触及时才可以被回收
  • 判断一个对象objA是否可以回收,至少要经历两次标记过程
  • 如果对象objA到GC Roots没有引用链,则进行一次标记
  • 进行筛选,判断此对象是否有必要执行finalize()方法
    ① 如果对象objA没有重写finalize()方法,或者finalize()方法已经被虚拟机调用过,则虚拟机视为“没有必要执行”,objA被判定为不可触及
    ② 如果对象objA重写了finalize()方法,且还未执行过,那么objA会被插入到F-Queue队列中,由一个虚拟机自动创建的、低优先级的Finalizer线程触发其finalize()方法执行
    ③finalize()方法是对象逃脱死亡的最后机会,稍后GC会对F-Queue队列中的对象进行第二次标记,如果objA在finalize()方法中与引用链上的任何一个对象建立了联系,那么在第二次标记时,objA被移出“即将回收”集合,之后,对象会再次出现没有引用存在情况,此时finalize()方法不会被再次调用,对象会直接变成不可触及的状态,一个对象的finalize方法只会被调用一次
/**
 * 测试object中finalize()方法,即对象finalization
 */
public class CanReliveObj {
    public static CanReliveObj obj;
    //此方法只调用1次
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("调用当前类重写finalize方法");
        obj = this;    //当前对象在finalize()方法中与引用链上的一个对象建立联系
    }

    public static void main(String[] args) throws InterruptedException {
        obj = new CanReliveObj();
        //对象第一次成功拯救自己
        obj = null;
        System.out.println("第1次gc");
        System.gc();
        Thread.sleep(2000);
        if (obj == null) {
            System.out.println("obj is dead");
        } else {
            System.out.println("obj is still alive");
        }
        System.out.println("第2次gc");
        obj = null;
        System.gc();
    }
}
//执行结果1次gc
调用当前类重写finalize方法
obj is still alive
第2次gc

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

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

相关文章

视频批量剪辑神器大揭秘:一键删减片头片尾,高效打造精彩视频内容!

在数字化时代的浪潮中,视频已经成为人们传递信息、分享生活的重要载体。无论是制作一部精美的宣传片,还是剪辑一段有趣的短视频,视频时长都是至关重要的因素。然而,很多视频创作者在调整视频时长时遇到了困难,耗费了大…

实体-联系图

为了把用户的数据要求清楚、准确地描述出来,系统分析员通常建立一个概念性的数据模型(也称为信息模型)。概念性数据模型是一种面向问题的数据模型,是按照用户的观点对数据建立的模型。它描述了从用户角度看到的数据,它反映了用户的现实环境, 而且与在软件系统中的实现方法无关。…

计算几何-扫描线算法

1、定义 计算几何中,扫描线算法(Sweep Line Algorithm)或平面扫描算法(Plane Sweep Algorithm)是一种算法模式,虚拟扫描线或扫描面来解决欧几里德空间中的各种问题,一般被用来解决图形面积&am…

Java面试八股之Synchronized和ReentrantLock的区别

Synchronized和ReentrantLock的区别 实现级别: synchronized是Java的一个关键字,属于JVM层面的原生支持,它通过监视器锁(Monitor)来实现同步控制,无需手动获取和释放锁。 ReentrantLock是java.util.conc…

免费插件集-illustrator插件-Ai插件-文本对象和文本段落互转

文章目录 1.介绍2.安装3.通过窗口>扩展>知了插件4.功能解释5.总结 1.介绍 本文介绍一款免费插件,加强illustrator使用人员工作效率,进行文本对象和文本段落互转。首先从下载网址下载这款插件 https://download.csdn.net/download/m0_67316550/878…

【408真题】2009-16

“接”是针对题目进行必要的分析,比较简略; “化”是对题目中所涉及到的知识点进行详细解释; “发”是对此题型的解题套路总结,并结合历年真题或者典型例题进行运用。 涉及到的知识全部来源于王道各科教材(2025版&…

计算机毕业设计Hadoop+Hive地震预测系统 地震数据分析可视化 地震爬虫 大数据毕业设计 Spark 机器学习 深度学习 Flink 大数据

2024 届本科毕业论文(设计) 基于Hadoop的地震预测的 分析与可视化研究 姓 名:____田伟情_________ 系 别:____信息技术学院___ 专 业:数据科学与大数据技术 学 号:__2011103094________ 指导…

Sam Altman微软Build 2024最新演讲:AI可能是下一个移动互联网

大家好,我是木易,一个持续关注AI领域的互联网技术产品经理,国内Top2本科,美国Top10 CS研究生,MBA。我坚信AI是普通人变强的“外挂”,所以创建了“AI信息Gap”这个公众号,专注于分享AI全维度知识…

vue+echart :点击趋势图中的某一点或是柱状图,出现弹窗,并传输数据

样式 在趋势图中点击某一个柱状图,出现下面的弹窗 代码实现 主要是在趋势图页面代码中,在初始化趋势图的设置中,添加对趋势图监听的点击方法 drawChart() {const chartData this.chartData;let option {};if (!chartData.xData?.len…

【LVGL_Linux安装NXP的Gui-Guider】

GUI Guider是恩智浦为LVGL开发了一个上位机GUI设计工具,可以通过拖放控件的方式设计LVGL GUI页面,加速GUI的设计。 虽然他只支持自家芯片,但是应用层我们可以直接拿来用作其他MCU上。 GUI-Guider 下载 NXP官网下载:链接&#xff1…

SpringBoot 集成 ChatGPT(附实战源码)

建项目 项目结构 application.properties openai.chatgtp.modelgpt-3.5-turbo openai.chatgtp.api.keyREPLACE_WITH_YOUR_API_KEY openai.chatgtp.api.urlhttps://api.openai.com/v1/chat/completionsopenai.chatgtp.max-completions1 openai.chatgtp.temperature0 openai.cha…

超值分享50个DFM模型格式的素人直播资源,适用于DeepFaceLive的DFM合集

50直播模型:点击下载 作为直播达人,我在网上购买了大量直播用的模型资源,包含男模女模、明星脸、大众脸、网红脸及各种稀缺的路人素人模型。现在,我将这些宝贵的资源整理成合集分享给大家,需要的朋友们可以直接点击下…

前端中 dayjs 时间的插件使用(在vue 项目中)

Day.js中文网 这是dayjs的中文文档 里面包括了使用方法 下面我来详细介绍一下这个插件的使用 Day.js 可以运行在浏览器和 Node.js 中。 一般咱直接是 npm 安装 npm install dayjs 目前应该使用的是Es6 的语法 import dayjs from dayjs 当前时间 直接调用 dayjs() 将返回…

中小企业纵横数智之海,华为云“航母级”平台护航

自古以来,中国人对探索未知世界的想象,便是“纵横四海”。 《尚书》最早记载了中国人的海洋观念,所谓“文命,敷于四海”。屈原在《九歌 云中君》中感叹,“览冀州兮有余,横四海兮焉穷”,希望云神带着他找寻海…

SRE视角下的DevOps:构建稳定高效的软件交付流程

SRE 和 DevOps 有什么区别和联系?本文对此进行了解读,并着重从 SRE 实践出发阐述了 DevOps 的建设思路。 SRE 就是在用软件工程的思维和方法论完成以前由系统管理员团队手动完成的工作。SRE 的职责是运维一个服务,该服务由一些相关的系统组件…

【MyBatis】MyBatis解析全局配置文件源码详解

目录 一、前言 思维导图概括 二、配置文件解析过程分析 2.1 配置文件解析入口 2.2 初始化XMLConfigBuilder 2.3 XMLConfigBuilder#parse()方法:解析全局配置文件 2.3.1 解析properties配置 2.3.2 解析settings配置 2.3.2.1 元信息对象(MetaClas…

Unity 自定义Web GL 发布模板

前言 使用讯飞语音识别时,发布Web GL 平台后需要在index.html 中添加相应的script 标签,但每次发布完添加比较麻烦,添加一个发布模板就可以不必每次发布完再手动添加修改。 实现 在Assets 文件夹下新建一个文件夹,重命名为WebG…

pygame raycasting纹理

插值原理 原理 color&#xff08;x&#xff09;(x-x1)/(x2-x1)(color2-color1)color1 x1<x<x2 假如说x伪3 那么color&#xff08;3-x1&#xff09;/(x2-x1)(color2-color1)color 可是图片纹理 这里不需要两种颜色&#xff0c;只需要获得碰撞点坐标后&#xff0c;如果…

腾讯面试:如何提升Kafka吞吐量?

面试题大全&#xff1a;www.javacn.site Kafka 是一个分布式流处理平台和消息系统&#xff0c;用于构建实时数据管道和流应用。它最初由 LinkedIn 开发&#xff0c;后来成为 Apache 软件基金会的顶级项目。 Kafka 特点是高吞吐量、分布式架构、支持持久化、集群水平扩展和消费组…

uniapp 对接 微信App/支付宝App 支付

相关文档&#xff1a;uni.requestPayment(OBJECT) | uni-app官网 示例代码&#xff1a; import qs from qsasync aliPay(){const { provider } await uni.getProvider({ service:payment })if(provider.includes(alipay)){uni.request({url:后端接口地址,data:{ //传参 },suc…