垃圾收集算法和垃圾收集器

2024-05-31 11:58
文章标签 算法 收集 收集器 垃圾

本文主要是介绍垃圾收集算法和垃圾收集器,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

垃圾收集算法

(画图的工具使用Gliffy,提供的模块有限使用3(高)*6(宽)的格子来描述算法的思想最理想的是宽为8的格子)

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

标记-清除算法是最基础的收集算法(之所以叫做基础,是因为后续算法都是基于此针对他的不足改进得到的),如同他的名字一样,分为两个阶段:标记阶段、清除阶段。标记阶段的任务是标记出所有需要被回收的对象,清除阶段就是回收被标记的对象所占用的空间
如下图:
标记清除

缺点或者不足:效率问题,标记清除效率都不高;另一个是空间问题,容易产生内存碎片,碎片太多可能会导致后续过程中需要为大对象分配空间时无法找到足够的空间而提前触发新的一次垃圾收集动作。

复制算法(Copying)

为了解决标记清除算法的效率问题,复制算法出现了。
他将可用内存按容量分为等大的两块,每次只是用其中一块,另一块作为保留区域,当一块内存用完了,就将存活的对象复制到另一块上,然后把已使用过的内存空间一次清理掉,如下图:
复制算法
复制算法实现简单,运行高效且不容易产生内存碎片
缺点不足:1.对内存空间的使用做出了高昂的代价,因为能够使用的内存缩减到原来的一半;2.复制算法的效率跟存活对象的数目多少有很大的关系,如果存活对象很多,那么复制算法的效率将会大大降低。

使用:由于他的特性,现代商业虚拟机采用复制算法来回收新生代,IBM公司研究表明,新生代中98%的对象是“朝生夕死”的,所以内存空间不必按照1:1的比例来分配,而是将内存划分为一块较大的Eden空间,两块较小的Survivor空间(HotSpot虚拟机默认Eden空间和Survivor空间的比例为8:1,80% + 10% = 90%),每次使用Eden空间和其中一块Survivor空间,当回收时,先将Eden和Survivor上存活的对象复制到,空闲的Survivor上,然后统一清理Eden空间和刚才是用过的Survivor空间。

但是,新生代98%的对象可回收针对一般场景,没有办法保证每次都有不多于10%的对象存活(当另一块Survivor空间不够用来存放上一次新生代收集的存活对象时,这些对象需要被分配进入担保内存),所以需要其他内存(老年代)进行分配担保。

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

和复制算法不同,标记-整理算法是针对老年代特点(存活率较高)的算法,充分利用空间,标记过程和“标记-清除算法”中的过程一样,但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存。如下图:

标记整理

老年代的垃圾回收器(例如 Serial Old,Parallel Old,不包括CMS,CMS使用的是标记清除法)都是采用这个算法,主要由于老年代的对象都比较持久,不是短暂的。

分代收集算法(Generational Collection)

根据对象存货周期的不同划分为几块,一般把堆分为新生代和老年代,还有一块区域被称为永久代即方法区(非堆,它的回收主要是针对废弃常量和无用类)。
在新生代中,每次垃圾回收时有大量对象死去,只有少量存活,那就选择“复制算法”,只需要付出少量对象的复制成本就可以完成收集。
在老年代中,存活率高,没有额外的空间对它分配担保,就必须使用“标记清除”或者“标记整理”来回收。

垃圾收集器

此处讨论的垃圾收集器基于《深入理解Java虚拟机》中所依赖的Java 7 update14之后的HotSpot虚拟机(在这个版本中正式提供了G1(Garbage First)收集器),一共七种,Serial和Serial Old在一起介绍。

先来一张图:

HotSpot虚拟机新生代老年代收集器划分搭配图

1.Serial/Serial Old

  Serial/Serial Old收集器是最基本最古老的收集器(JDK1.3.1之前版本),它是一个单线程收集器,并且在它进行垃圾收集时,必须暂停所有工作线程(Stop The World)。Serial收集器是针对新生代的收集器,采用的是Copying算法,Serial Old收集器是针对老年代的收集器,采用的是Mark-Compact算法。它的优点是实现简单高效,但是缺点是会给用户带来停顿。

2.ParNew

  ParNew收集器是Serial收集器的多线程版本,使用多个线程进行垃圾收集。

3.Parallel Scavenge(吞吐量优先收集器)

  Parallel Scavenge收集器是一个新生代的多线程收集器(并行收集器),它在回收期间不需要暂停其他用户线程,其采用的是Copying算法,该收集器与前两个收集器有所不同,它主要是为了达到一个可控的吞吐量(吞吐量:就是CPU用于运行用户代码的时间与CPU总耗时的比值 吞吐量 = 运行用户代码时间/(运行用户代码时间+垃圾回收时间),比如虚拟机总共运行100分钟,垃圾回收用了1分钟,吞吐量是99%吞吐量越高应用程序交互性越好)。

4.Parallel Old

  Parallel Old是Parallel Scavenge收集器的老年代版本(并行收集器),使用多线程和Mark-Compact算法。(JDK1.6中提供)

5.CMS

  CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,并发收集、低停顿,它是一种并发收集器,采用的是Mark-Sweep算法。
步骤交标记清除复杂有四步:
①初始标记
②并发标记
③重新标记
④并发清除
缺点:
①对CPU资源敏感(占用CPU资源较多)。
②无法处理浮动辣鸡(Floating Garbage,即在垃圾回收是又产生的一部分垃圾,在当次回收时无法处理只能留待下次处理),可能出现Concurrent Mode Failure失败,从而触发另一次FullGC。
③标记清除产生大量碎片。
6.G1
详细参考:新一代垃圾回收算法增量收集算法和G1收集算法
  G1(Garbage First)收集器是当今收集器技术发展最前沿的成果,它是一款面向服务端应用的收集器,它能充分利用多CPU、多核环境。因此它是一款并行与并发收集器,并且它能建立可预测的停顿时间模型。

特点:并行与并发  分代收集  空间整合  可预测停顿
步骤: ①初始标记 ②并发标记 ③最终标记 ④筛选回收

  下面补充一下关于内存分配方面的东西:
  
  对象的内存分配,往大方向上讲就是在堆上分配,对象主要分配在新生代的Eden Space和From Space,少数情况下会直接分配在老年代。如果新生代的Eden Space和From Space的空间不足,则会发起一次GC,如果进行了GC之后,Eden Space和From Space能够容纳该对象就放在Eden Space和From Space。在GC的过程中,会将Eden Space和From Space中的存活对象移动到To Space,然后将Eden Space和From Space进行清理。如果在清理的过程中,To Space无法足够来存储某个对象,就会将该对象移动到老年代中。在进行了GC之后,使用的便是Eden space和To Space了,下次GC时会将存活对象复制到From Space,如此反复循环。当对象在Survivor区躲过一次GC的话,其对象年龄便会加1,默认情况下,如果对象年龄达到15岁,就会移动到老年代中。

  一般来说,大对象会被直接分配到老年代,所谓的大对象是指需要大量连续存储空间的对象,最常见的一种大对象就是大数组,比如:

  byte[] data = new byte[4*1024*1024]

  这种一般会直接在老年代分配存储空间。

  当然分配的规则并不是百分之百固定的,这要取决于当前使用的是哪种垃圾收集器组合和JVM的相关参数。

参考:
《深入理解Java虚拟机》第二版
推荐要看的虚拟机方面的知识
垃圾回收算法和垃圾收集器
新一代垃圾回收算法增量收集算法和G1收集算法

这篇关于垃圾收集算法和垃圾收集器的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1017864

相关文章

代码随想录算法训练营:12/60

非科班学习算法day12 | LeetCode150:逆波兰表达式 ,Leetcode239: 滑动窗口最大值  目录 介绍 一、基础概念补充: 1.c++字符串转为数字 1. std::stoi, std::stol, std::stoll, std::stoul, std::stoull(最常用) 2. std::stringstream 3. std::atoi, std

人工智能机器学习算法总结神经网络算法(前向及反向传播)

1.定义,意义和优缺点 定义: 神经网络算法是一种模仿人类大脑神经元之间连接方式的机器学习算法。通过多层神经元的组合和激活函数的非线性转换,神经网络能够学习数据的特征和模式,实现对复杂数据的建模和预测。(我们可以借助人类的神经元模型来更好的帮助我们理解该算法的本质,不过这里需要说明的是,虽然名字是神经网络,并且结构等等也是借鉴了神经网络,但其原型以及算法本质上还和生物层面的神经网络运行原理存在

大林 PID 算法

Dahlin PID算法是一种用于控制和调节系统的比例积分延迟算法。以下是一个简单的C语言实现示例: #include <stdio.h>// DALIN PID 结构体定义typedef struct {float SetPoint; // 设定点float Proportion; // 比例float Integral; // 积分float Derivative; // 微分flo

LeetCode 算法:二叉树的中序遍历 c++

原题链接🔗:二叉树的中序遍历 难度:简单⭐️ 题目 给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。 示例 1: 输入:root = [1,null,2,3] 输出:[1,3,2] 示例 2: 输入:root = [] 输出:[] 示例 3: 输入:root = [1] 输出:[1] 提示: 树中节点数目在范围 [0, 100] 内 -100 <= Node.

【Java算法】滑动窗口 下

​ ​    🔥个人主页: 中草药 🔥专栏:【算法工作坊】算法实战揭秘 🦌一.水果成篮 题目链接:904.水果成篮 ​ 算法原理 算法原理是使用“滑动窗口”(Sliding Window)策略,结合哈希表(Map)来高效地统计窗口内不同水果的种类数量。以下是详细分析: 初始化:创建一个空的哈希表 map 用来存储每种水果的数量,初始化左右指针 left

ROS2从入门到精通4-4:局部控制插件开发案例(以PID算法为例)

目录 0 专栏介绍1 控制插件编写模板1.1 构造控制插件类1.2 注册并导出插件1.3 编译与使用插件 2 基于PID的路径跟踪原理3 控制插件开发案例(PID算法)常见问题 0 专栏介绍 本专栏旨在通过对ROS2的系统学习,掌握ROS2底层基本分布式原理,并具有机器人建模和应用ROS2进行实际项目的开发和调试的工程能力。 🚀详情:《ROS2从入门到精通》 1 控制插

算法与数据结构面试宝典——回溯算法详解(C#,C++)

文章目录 1. 回溯算法的定义及应用场景2. 回溯算法的基本思想3. 递推关系式与回溯算法的建立4. 状态转移方法5. 边界条件与结束条件6. 算法的具体实现过程7. 回溯算法在C#,C++中的实际应用案例C#示例C++示例 8. 总结回溯算法的主要特点与应用价值 回溯算法是一种通过尝试各种可能的组合来找到所有解的算法。这种算法通常用于解决组合问题,如排列、组合、棋盘游

【图像识别系统】昆虫识别Python+卷积神经网络算法+人工智能+深度学习+机器学习+TensorFlow+ResNet50

一、介绍 昆虫识别系统,使用Python作为主要开发语言。通过TensorFlow搭建ResNet50卷积神经网络算法(CNN)模型。通过对10种常见的昆虫图片数据集(‘蜜蜂’, ‘甲虫’, ‘蝴蝶’, ‘蝉’, ‘蜻蜓’, ‘蚱蜢’, ‘蛾’, ‘蝎子’, ‘蜗牛’, ‘蜘蛛’)进行训练,得到一个识别精度较高的H5格式模型文件,然后使用Django搭建Web网页端可视化操作界面,实现用户上传一

【数据结构与算法 经典例题】使用队列实现栈(图文详解)

💓 博客主页:倔强的石头的CSDN主页               📝Gitee主页:倔强的石头的gitee主页    ⏩ 文章专栏:《数据结构与算法 经典例题》C语言                                   期待您的关注 ​​ 目录  一、问题描述 二、前置知识 三、解题思路 四、C语言实现代码 🍃队列实现代码:

算法11—判断一个树是不是二叉查询树

问题: 给定一个二叉树,判断它是否是二叉查询树。 思路: 要判断是否是二叉查询树,标准就是看每一个节点是否满足:1、左节点及以下节点的值比它小;2、右节点及以下节点的值比它大。当然,前提是子节点都存在的情况。所以,我们需要从根节点不断向下递归,只要所有节点都满足,那么就是BST,否则,就不是。 代码: [java]  view plain copy pri