垃圾收集器与内存分配策略

news2024/11/18 15:52:35

概述

垃圾收集需要完成的三件事情:

  1. 哪些内存需要回收?
  2. 什么时候回收?
  3. 如何回收?

判断对象是都存活的算法:
  1. 引用计数法:在对象中添加一个引用计数器,每当有一个地方引用时,计数器值就加1;引用失效时,计数器值就减1。任何计数器为0的对象就是不可能再被使用的。但该算法无法解决循环引用问题。
  2. 可达性分析算法:通过一系列可称为“GC Roots”的根对象作为起点集,从这些节点开始,根据引用关系向下搜索,搜索过程中所走过的路径叫做“引用链”。
再谈引用:
  1. 强引用:类似于“new Object()”这种引用关系。无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象
  2. 软引用:描述一些有用,但非必须的对象。软引用关联着的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。使用SoftRefrence类来实现软引用
  3. 弱引用:也是描述非必须对象,比软引用更弱,被弱引用关联的对象只能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。使用WeakRefrence类来实现弱引用。
  4. 虚引用:最弱的一种引用关系。无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的 唯一目的是为了能在这个对象被收集器回收时收到一个系统通知。使用PhantomRefrence类来实现虚引用。

一次对象自我拯救的演示:

public class FinalizeEscapeGC {
    public static FinalizeEscapeGC SAVE_HOOK = null;

    public void isAlive() {
        System.out.println("yes, I am still alive :)");
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize method executed!");
        FinalizeEscapeGC.SAVE_HOOK = this;
    }

    public static void main(String[] args) throws InterruptedException {
        SAVE_HOOK = new FinalizeEscapeGC();

        // 对象第一次成功拯救自己
        SAVE_HOOK = null;
        System.gc();
        // 因为finalize()优先级很低,暂停0.5秒,以等待它执行
        Thread.sleep(500);
        if (SAVE_HOOK != null) {
            SAVE_HOOK.isAlive();
        } else {
            System.out.println("no, I am dead :(");
        }

        // 下面这段代码与上面完全相同,但是这次却自救失败了
        SAVE_HOOK = null;
        System.gc();
        // 因为finalize()优先级很低,暂停0.5秒,以等待它执行
        Thread.sleep(500);
        if (SAVE_HOOK != null) {
            SAVE_HOOK.isAlive();
        } else {
            System.out.println("no, I am dead :(");
        }
    }
}

---- 运行结果:

finalize method executed!
yes, I am still alive :)
no, I am dead :(

上面示例代码中有两段完全一样的代码片段,执行结果却是一次逃脱成功,一次失败了,这里任何一个finalize()方法都只会被系统自动调用一次,如果对象面临下一次回收,它的finalize()方法就不会再次执行。

分代收集理论
  1. 弱分代假说:绝大部分对象都是朝生夕灭的
  2. 强分代假说:熬过越多次垃圾收集过程的都想越难以消亡

不同分代的垃圾收集名词:

  1. 部分收集(Partial GC):指目标不是完整收集整个Java堆的垃圾收集,其中又分为:
    1. 新生代收集(Minor GC/Yong GC):指目标只是新生代的收集
    2. 老年代收集(Major GC/Old Gc):指目标只是老年代的收集。目前只有CMS收集器会有单独收集老年代的行为。
    3. 混合收集(Mixed GC):指目标时收集整个新生代以及部分老年代的垃圾收集器。目前只有G1收集器会有这种行为。
  1. 整堆收集(Full GC):收集整个Java堆和方法区的垃圾收集。

垃圾收集算法

标记-清除算法

算法分为“标记”和“清除”两个阶段:首先标记出需要回收的对象,在标记完成后,统一回收掉所有被标记的对象。也可以反过来,标记存活的对象,统一回收未被标记的对象。主要有如下两个缺点:

    1. 执行效率不稳定,标记和清除的执行效率会随对象数量的增长而降低
    2. 内存空间的碎片化问题,若需要分配较大对象时,由于无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。
标记-复制算法

新生代采用该算法。半区复制,可用内存缩小为了原来的一半,会造成较大的空间浪费。

HotSpot虚拟机的Serial、ParNew等新生代分为一块较大的Eden空间和两块较小的Survivor空间,每次分配内存只使用Eden和其中一块Survivor空间。发生垃圾收集时,将Eden和Survivor中仍然存活的对象一次性复制到另外一块Survivor空间上,然后直接清理掉Eden和用过的Survivor空间,HotSpot虚拟机默认的Eden和Survivor的大小比例为8 : 1,也就是只有10%的新生代是被“浪费”的。若Survivor空间不足以容纳一次Minor GC之后存活的对象,则需要依赖其它内存区域(实际上大多是老年代)进行分配担保。

标记-整理算法

与标记-清除算法的本质差异在于标记-整理算法是一种移动式的回收算法。是否移动存活对象是一项优缺点并存的风险决策。

如果移动存活对象,尤其是在老年代这种每次回收都有大量对象存活的区域,移动存活对象并更新引用这些对象的地方,将会是一种极为负重的操作,而且这种对象移动操作必须全程暂停用户应用程序才能进行。也被形象称为“Stop The World”。若不移动存活的对象,则空间碎片化问题就只能依赖更为复杂的内存分配器和内存访问器来解决,由于内存访问使用户程序最为频繁的操作,若在这个环节增加了额外的负担,则会大大影响应用程序的吞吐量。

HotSpt虚拟机里面关注吞吐量的Parallel Scavenge收集器是基于标记-整理算法的。而关注延迟的CMS收集器则是基于标记-清除算法的。另外还有一种”和稀泥式“的解决方案,可以让虚拟机平时大多数时间都是采用标记-清除算法,暂时容忍内存碎片的存在,知道内存空间的碎片化程度已经大到影响对象分配时,再采用标记-整理算法收集一次,以获得规整的内存空间。CMS收集器就是采用的这种方式。

经典的垃圾收集器

如果说垃圾收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的实践者。

上图展示了七种作用于不同分代的收集器。如果两个收集器之间存在连线,就说明他们可以搭配使用。这个关系不是一成不变的,JDK8中已将Serial + CMS、ParNew + Serial Old这两个组合申明为废弃。

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

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

相关文章

python基础教程:异常处理

嗨喽~大家好呀,这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 有时候我们在写程序的时候会出现错误或者异常,导致程序终止,如下这个例子: #!/usr/bin/env python a 2/0 print(a)结果提示如…

Visual Studio Professional 2019 软件安装教程(附安装包下载)

Microsoft Visual Studio 是一个非常强大的集成开发环境(IDE),适用于 Windows 上的 .NET 和 C 开发人员。它提供了一系列丰富的工具和功能,可以提升和增强软件开发的每个阶段。 Visual Studio IDE 是一个创意启动板,可…

C++ 多线程编程和同步机制:详解和实例演示

C中的多线程编程和同步机制使得程序员可以利用计算机的多核心来提高程序的运行效率和性能。本文将介绍多线程编程和同步机制的基本概念和使用方法。 多线程编程基础 在C中&#xff0c;使用<thread>库来创建和管理线程。线程可以通过函数、成员函数或者Lambda表达式来实现…

基于C#使用winform技术的游戏平台的实现【C#课程设计】

基于C#使用winform技术的游戏平台的实现【C#课程设计】 说明项目结构项目运行截图及实现的功能 部分代码一些说明(个人觉得一些难点的说明)一、ListView &#xff0c;ImageList 的综合使用二、图片上传以及picturebox 图片的动态替换三、图表插件的使用四、SQL工具类封装五、高…

最新哔哩哔哩邮箱绑定接口签名JS逆向分析

本章教程主要逆向分析 哔哩哔哩邮箱绑定接口biliCSRF 和mid 参数。 教程仅供学习参考,请勿滥用,由此带来的法律责任需由自己承担。 目录 一、接口参数分析 二、签名加密代码 三、滑块验证码 一

LabVIEW应用开发——控件的使用(四)

接上文&#xff0c;这篇介绍时间控件。 LabVIEW应用开发——控件的使用&#xff08;三&#xff09; 1、时间控件Time Stamp control 在日常软件开发场景中&#xff0c;时间也是一种常用的控件&#xff0c;用于表达当前时间的显示、对下设置时间、时间同步等等场景。LabVIEW专门…

Redis主从模式(一)----搭建主从结构并演示

目录 一, 主从模式 1.1 单个Redis服务器可能存在的问题 1.2 单点问题 1.3 什么是主从模式 概念 图示 二, 演示Redis的主从复制 2.1 Redis-server进程 2.2 建立复制 1. 首先将redis.conf配置文件复制一份并修改daemonize 为 yes 2.修改配置文件中的端口号 3. 分别在…

力扣每日一题63:不同路径||

题目描述&#xff1a; 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish”&#xff09;。 现在考虑网格中有障碍物…

RK3568平台开发系列讲解(应用篇)串口应用编程之串口介绍

🚀返回专栏总目录 文章目录 一、串口介绍1.1、数据传输方式1.2、数据格式1.3、波特率1.4、硬件流控制和软件流控制1.5、错误检测1.6、串口编程二、串口设备节点介绍沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 串口设备是嵌入式开发中最常用的外设之一,通过串口…

2023年信息科学与工程学院学生科协第一次前端培训

目录 一、前端是什么&#xff1f;前端能做什么&#xff1f;前端需要做什么&#xff1f;现阶段如何理解前端 二、前端学习路线html是什么&#xff1f;css是什么&#xff1f;什么是jshtml、css以及js关系掌握三种语言之后的学习路线 三、HTML基础语法标题段落文本换行文本标签图像…

协程和 C++ Boost库的Coroutine2

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 协程和 C Boost库的Coroutine2 摘要为什么不是boost.coroutine&#xff1f; 线程与协程为什么不介绍C20标准的协程C协程与golang的goroutine 二、使用步骤1.引入库2.核心类单…

作为程序员,很多时候容易急眼,如何缓解?

程序员在工作中面临着高压和快节奏的环境&#xff0c;容易因为紧张的工作节奏、复杂的技术问题或与团队的沟通问题而感到焦虑和急躁。下面提供一些策略来帮助缓解这种情况&#xff1a; 1. 定时休息 遵循“番茄工作法”或其他时间管理技术&#xff0c;每工作25分钟后休息5分钟&…

“第四十九天” 机组

各个硬件的工作原理&#xff1a; 主存储器包括&#xff1a;存储体、MAR、MDR&#xff1b; MAR&#xff1a;存储地址寄存器 &#xff0c;MAR的位数反映存储单元的个数&#xff1b; MDR &#xff1a; 存储数据存储器&#xff0c;MDR位数等于存储字长&#xff1b; 数据在存储体…

【Opencv】OpenCV使用CMake和MinGW的编译安装出错解决

编译时出现的错误&#xff1a; mingw32-make[1]: *** [modules/core/CMakeFiles/opencv_core.dir/all] Error 2 Makefile:161: recipe for target ‘all’ failed mingw32-make: *** [all] Error 2解决方法&#xff1a; 根据贴吧老哥的解答&#xff0c;发现是mingw版本有问题导…

合并有序链表C++递归

题目描述 21. 合并两个有序链表 - 力扣&#xff08;LeetCode&#xff09; 解题思路 我们这里使用递归的思路来解题&#xff1a; 首先我们要分析题目的子问题在哪&#xff0c;对于这个问题我们先思考如何将两个只有一个节点的有序单链表合并&#xff1a; ①我们可以先判断哪一个…

DCU上如何运行大模型以及用到的docker命令

第一步&#xff1a;需要连接到官方(https://developer.hpccube.com/)提供的vpn 第二步&#xff1a;通过termius进入到项目 第三步&#xff1a;遇到问题 1.docker空间太小了&#xff0c;得换地方&#xff1a;参考这个centos设置docker 目录_mob64ca12f73101的技术博客_51CTO博…

寻找特殊年号

系列文章目录 进阶的卡莎C_睡觉觉觉得的博客-CSDN博客数1的个数_睡觉觉觉得的博客-CSDN博客双精度浮点数的输入输出_睡觉觉觉得的博客-CSDN博客足球联赛积分_睡觉觉觉得的博客-CSDN博客大减价(一级)_睡觉觉觉得的博客-CSDN博客小写字母的判断_睡觉觉觉得的博客-CSDN博客纸币(C…

正点原子嵌入式linux驱动开发——Linux INPUT子系统

按键、鼠标、键盘、触摸屏等都属于输入(input)设备&#xff0c;Linux内核为此专门做了一个叫做input子系统的框架来处理输入事件。输入设备本质上还是字符设备&#xff0c;只是在此基础上套上了input框架&#xff0c;用户只需要负责上报输入事件&#xff0c;比如按键值、坐标等…

python二次开发Solidworks:读取样条曲线数据

目录 1、草图段对象 2、VBA代码分析 3、python代码实现 样条曲线&#xff08;spline curve&#xff09;是数学术语&#xff0c;是一种特殊的参数曲线&#xff0c;由一组控制点通过曲线拟合的方式生成。样条一词源于船舶建造中的一种临时性辅助支架&#xff0c;后来被引入计算…

基于卷的磁盘扫描算法设计

1、设计目的 常规情况下&#xff0c;当我们扫描计算机的硬盘时&#xff0c; 通常会使用诸如FindFirstFile/FindNextFile(Windows)&#xff0c;或者opendir/readdir(Linux)遍历扫描的目录。 一般情形下&#xff0c;由于文件数量相对较少&#xff0c;文件夹层次低&#xff0c;扫…