python虚拟机集锦(2)-垃圾收集算法(2)

news2024/9/22 17:24:25

目录

    • 识别参考循环
    • 为什么移动无法访问的对象更好
    • 正在销毁无法访问的对象
    • 优化:世代
    • 收集最古老的一代

识别参考循环

当GC启动时,它在第一个链接列表中拥有所有要扫描的容器对象。目标是移动所有无法到达的对象。由于大多数对象都是可访问的,因此移动不可访问的对象要有效得多,因为这需要更少的指针更新。

每个支持垃圾收集的对象都将有一个额外的引用计数字段,该字段在算法启动时初始化为该对象的引用计数(图中的gc_ref)。这是因为算法需要修改引用计数来进行计算,这样解释器就不会修改实际引用计数字段。

在这里插入图片描述
然后,GC遍历第一个列表中的所有容器,并将容器引用的任何其他对象的GC_ref字段减一。这样做可以利用容器类中的tp_traverse槽(使用C API实现或由超类继承)来了解每个容器引用的对象。扫描完所有对象后,只有从“要扫描的对象”列表外部引用的对象的gc_refs>0。
在这里插入图片描述
请注意,gc_refs==0并不意味着对象不可访问。这是因为从外部可访问的另一个对象(gc_refs>0)仍然可以引用它。例如,我们示例中的link_2对象以gc_refs==0结尾,但仍然被从外部可到达的link_1对象引用。为了获得一组真正无法访问的对象,垃圾收集器使用tp_traverse槽重新扫描容器对象;这次使用了一个不同的遍历函数,它将gcrefs==0的对象标记为“暂时不可访问”,然后将它们移动到暂时不可到达的列表中。下图描述了当GC处理了link_3和link_4对象但尚未处理link_1和link_2时列表的状态。
在这里插入图片描述

然后GC扫描下一个link_1对象。因为它具有gc_refs==1,gc不会做任何特殊的事情,因为它知道它必须是可访问的(并且已经在可访问列表中):
在这里插入图片描述

当GC遇到一个可访问的对象(GC_refs>0)时,它使用tp_traverse槽遍历其引用,以找到从它可访问的所有对象,将它们移动到可访问对象列表的末尾(它们最初开始的位置),并将其GC_refs字段设置为1。这是下面链接2和链接3的情况,因为它们可以从链接1访问。从上一个图像中的状态,在检查了link_1引用的对象之后,GC知道link_3毕竟是可访问的,因此它被移回原始列表,其GC_refs字段被设置为1,这样如果GC再次访问它,它就会知道它是可访问。为了避免访问一个对象两次,GC标记所有已经访问过一次的对象(通过取消设置PREV_MASK_COLLECTING标志),这样,如果已经处理过的对象被其他对象引用,GC就不会处理它两次。
在这里插入图片描述请注意,垃圾收集器将再次访问标记为“暂时不可访问”并随后移回可访问列表的对象,因为现在也需要处理该对象的所有引用。这个过程实际上是对对象图的广度优先搜索。一旦扫描完所有对象,GC就知道暂时无法访问列表中的所有容器对象都是无法访问的,因此可以进行垃圾收集。
实际上,需要注意的是,任何一种方法都不需要递归,也不需要与对象数量、指针数量或指针链长度成比例的额外内存。除了满足内部C需求的O(1)存储外,对象本身包含GC算法所需的所有存储。

为什么移动无法访问的对象更好

在大多数对象通常是可访问的前提下移动不可访问的对象听起来很合乎逻辑,直到你想一想:它付出的代价其实并不明显。

假设我们按顺序创建对象A、B、C。他们以同样的顺序出现在年轻一代中。如果B指向A,C指向B,C可以从外部到达,那么在算法运行的第一步之后,调整后的引用计数将分别为0、0和1,因为从外部唯一可以到达的对象是C。

当算法的下一步找到A时,A被移动到不可访问列表。第一次遇到B时也是如此。然后遍历C,将B移回可访问列表。最终遍历B,然后将A移回可访问列表。

因此,可到达的对象B和A都会移动两次,而不是完全不移动。为什么这是一场胜利?相反,移动可到达对象的简单算法将移动A、B和C各一次。关键是这种舞蹈将物体按C、B、A的顺序排列,这与原来的顺序相反。在所有后续扫描中,它们都不会移动。由于大多数对象不在循环中,这可以在以后的集合中节省无限数量的移动。唯一一次成本可能更高的是第一次扫描链条。

正在销毁无法访问的对象

一旦GC知道了不可访问对象的列表,一个非常微妙的过程就开始于完全销毁这些对象的目标。大致上,流程遵循以下步骤:

处理和清理薄弱参考(如有)。如果不可访问集合中的对象将被销毁,并且具有带有回调的弱引用,则需要执行这些回调。这个过程非常微妙,因为任何错误都可能导致处于不一致状态的对象被重新激活或由回调调用的某些Python函数访问。此外,还属于不可访问集的弱引用(对象及其弱引用处于不可访问的循环中)需要立即清除,而不执行回调。否则,它将在稍后调用tp_clear插槽时触发,从而造成破坏。忽略弱引用的回调是可以的,因为对象和weakref都将消失,所以说弱引用将首先消失是合理的。

如果某个对象具有旧的终结器(tp_del槽),请将它们移动到gc.garbage列表中。

调用终结器(tp_finalize slot)并将对象标记为已完成,以避免在它们重新启动或其他终结器已首先删除对象时调用它们两次。

处理复活的物体。如果某些对象已经复活,GC将通过再次运行循环检测算法找到仍然无法访问的新对象子集,并继续执行这些操作。

调用每个对象的tp_clear槽,使所有内部链接断开,引用计数降至0,从而触发所有不可访问对象的销毁。

一旦GC知道了不可访问对象的列表,一个非常微妙的过程就开始于完全销毁这些对象的目标。大致上,流程遵循以下步骤:

处理和清理薄弱参考(如有)。如果不可访问集合中的对象将被销毁,并且具有带有回调的弱引用,则需要执行这些回调。这个过程非常微妙,因为任何错误都可能导致处于不一致状态的对象被重新激活或由回调调用的某些Python函数访问。此外,还属于不可访问集的弱引用(对象及其弱引用处于不可访问的循环中)需要立即清除,而不执行回调。否则,它将在稍后调用tp_clear插槽时触发,从而造成破坏。忽略弱引用的回调是可以的,因为对象和weakref都将消失,所以说弱引用将首先消失是合理的。

如果某个对象具有旧的终结器(tp_del槽),请将它们移动到gc.garbage列表中。

调用终结器(tp_finalize slot)并将对象标记为已完成,以避免在它们重新启动或其他终结器已首先删除对象时调用它们两次。

处理复活的物体。如果某些对象已经复活,GC将通过再次运行循环检测算法找到仍然无法访问的新对象子集,并继续执行这些操作。

调用每个对象的tp_clear槽,使所有内部链接断开,引用计数降至0,从而触发所有不可访问对象的销毁。

优化:世代

为了限制每次垃圾收集所需的时间,GC使用了一种流行的优化:生成。这个概念背后的主要思想是假设大多数对象的寿命很短,因此可以在创建后不久收集。事实证明,这与许多Python程序的实际情况非常接近,因为许多临时对象的创建和销毁速度非常快。对象越老,它越不可能变得无法访问。

为了利用这一事实,所有容器对象都被划分为三个空间/代。每个新对象都从第一代(第0代)开始。上一个算法仅在特定世代的对象上执行,如果某个对象在其世代的集合中存活下来,它将被移动到下一代(第1代),在那里,它将不太频繁地进行收集调查。如果同一个对象在新一代(第1代)的另一轮GC中存活下来,它将被移动到上一代(2代),在那里它将被检查的次数最少。

为了决定何时运行,收集器会跟踪自上次收集以来对象分配和释放的数量。当分配数减去释放数超过threshold_0时,收集开始。最初只检查0代。如果自检查了第1代以来,第0代已检查了超过阈值1次,则也检查第1代。对于第二代来说,事情有点复杂;有关详细信息,请参阅收集最旧的一代。可以使用gc.get_threshold()函数检查这些阈值:

为了限制每次垃圾收集所需的时间,GC使用了一种流行的优化:生成。这个概念背后的主要思想是假设大多数对象的寿命很短,因此可以在创建后不久收集。事实证明,这与许多Python程序的实际情况非常接近,因为许多临时对象的创建和销毁速度非常快。对象越老,它越不可能变得无法访问。

为了利用这一事实,所有容器对象都被划分为三个空间/代。每个新对象都从第一代(第0代)开始。上一个算法仅在特定世代的对象上执行,如果某个对象在其世代的集合中存活下来,它将被移动到下一代(第1代),在那里,它将不太频繁地进行收集调查。如果同一个对象在新一代(第1代)的另一轮GC中存活下来,它将被移动到上一代(2代),在那里它将被检查的次数最少。

为了决定何时运行,收集器会跟踪自上次收集以来对象分配和释放的数量。当分配数减去释放数超过threshold_0时,收集开始。最初只检查0代。如果自检查了第1代以来,第0代已检查了超过阈值1次,则也检查第1代。对于第二代来说,事情有点复杂;有关详细信息,请参阅收集最旧的一代。可以使用gc.get_threshold()函数检查这些阈值:

import gc
gc.get_threshold()
(700, 10, 10)

可以使用gc.get_objects(generation=NUM)函数检查这些代的内容,并且可以通过调用gc.collect(generation=NUM)在一代中专门触发集合。

import gc
class MyObj:
    pass


# Move everything to the last generation so it's easier to inspect
# the younger generations.

gc.collect()
0

# Create a reference cycle.

x = MyObj()
x.self = x

# Initially the object is in the youngest generation.

gc.get_objects(generation=0)
[..., <__main__.MyObj object at 0x7fbcc12a3400>, ...]

# After a collection of the youngest generation the object
# moves to the next generation.

gc.collect(generation=0)
0
gc.get_objects(generation=0)
[]
gc.get_objects(generation=1)
[..., <__main__.MyObj object at 0x7fbcc12a3400>, ...]

收集最古老的一代

除了各种可配置阈值之外,如果long_lived_pending/long_lived_total比率高于给定值(硬连线为25%),GC仅触发最旧一代的完整集合。原因是,虽然“非完整”集合(即年轻一代和中年一代的集合)将始终检查大致相同数量的对象(由上述阈值确定),但完整集合的成本与长寿命对象的总数成比例,这实际上是无限的。事实上,有人指出,每创建一个<常量>的对象就进行一次完整的收集,这会导致工作负载的性能急剧下降,因为工作负载包括创建和存储大量的长期对象(例如,构建一个GC跟踪对象的大列表将显示二次性能,而不是预期的线性性能)。相反,使用上述比率,会产生对象总数的摊销线性性能(其效果可以这样总结:“随着对象数量的增加,每次完整的垃圾收集成本越来越高,但我们做的垃圾越来越少”)。

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

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

相关文章

【回首2022,展望2023,兔年你好!】

兔年到&#xff0c;新年好&#xff01;提前祝各位程序猿新年好&#xff01;新的一年&#xff0c;意味着新的开始。回顾2022&#xff0c;我学习从0基础萌新&#xff0c;蜕变成学习完c语言&#xff0c;有了基础代码能力的新手。从第一个printf出来hello world&#xff0c;打印到屏…

jpg图片损坏怎么修复?可以试试这个方法

生活中&#xff0c;相信不少小伙伴在使用电脑或手机时&#xff0c;难免会遇到这样的问题&#xff0c;就是保存的图片损坏了&#xff0c;看不到图像了。在出现图像损坏后&#xff0c;我们经常问自己的第一个问题是&#xff0c;有什么办法可以修复它们&#xff0c;之前小编也是遇…

回收租赁商城系统功能拆解11讲-会员分组

回收租赁系统适用于物品回收、物品租赁、二手买卖交易等三大场景。 可以快速帮助企业搭建类似闲鱼回收/爱回收/爱租机/人人租等回收租赁商城。 回收租赁系统支持智能评估回收价格&#xff0c;后台调整最终回收价&#xff0c;用户同意回收后系统即刻放款&#xff0c;用户微信零…

MySQL监控(三):Grafana入门

1.官方文档 Grafana 安装包下载地址 基于Prometheus、Grafana的可视化监控实践 grafana安装部署、自定义主题、dashboard模板导入 Grafana Dashboard下载 2.安装 我是通过压缩包的方式安装的&#xff0c;官网也有给出yum的安装方式。 wget https://dl.grafana.com/enterpri…

【Javascript】面向对象编程,this,原型与原型链,类与实例,class,Maps

❤️ Author&#xff1a; 老九 ☕️ 个人博客&#xff1a;老九的CSDN博客 &#x1f64f; 个人名言&#xff1a;不可控之事 乐观面对 &#x1f60d; 系列专栏&#xff1a; 文章目录对象中的方法/thisthis使用bind函数原型原型链类与实例classclass语法补充Map对象中的方法/this …

【设计模式】结构型模式·代理模式

学习汇总入口【23种设计模式】学习汇总(数万字讲解体系思维导图) 写作不易&#xff0c;如果您觉得写的不错&#xff0c;欢迎给博主来一波点赞、收藏~让博主更有动力吧&#xff01; 一.概念 由于某些原因访问对象不适合或者不能直接引用目标对象&#xff0c;这时可以为目标对象…

可视化VIT中的注意力

2022年&#xff0c; Vision Transformer (ViT)成为卷积神经网络(cnn)的有力竞争对手&#xff0c;卷积神经网络目前是计算机视觉领域的最先进技术&#xff0c;广泛应用于许多图像识别应用。在计算效率和精度方面&#xff0c;ViT模型超过了目前最先进的(CNN)几乎四倍。 ViT是如何…

【C++】面向对象---多态(万字详解)

&#x1f525;&#x1f525; 欢迎来到小林的博客&#xff01;&#xff01;       &#x1f6f0;️博客主页&#xff1a;✈️小林爱敲代码       &#x1f6f0;️文章专栏&#xff1a;✈️小林的C之路       &#x1f6f0;️欢迎关注&#xff1a;&#x1f44d…

SIMD性能优化

文章目录前言MMXSSEAVX使用内置函数使用SSE/AVX命名规则SSE/AVX操作类别实战汇编使用优化前代码详解优化后代码详解引用文章#mermaid-svg-sNu7iEVk2jpyjjtX {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#fff;}#mermaid-svg-sNu7iEVk2…

【黑马学成在线2023版】解决P7创建父工程时Maven的pom文件依赖爆红问题(亲测有效)

本期目录问题描述解决方案问题描述 感谢黑马贡献的高质量视频教程《学成在线》微服务项目。笔者在学到《P7-创建父工程基础工程》时&#xff0c;直接粘贴黑马老师的 pom 文件中的依赖会出现依赖的 <artifactId> 和 <version> 爆红&#xff0c;显示无法找到依赖的错…

vite学习笔记

vite 1.vite是什么&#xff1f; 基于ES-Module的前端构建工具 2.为什么选择vite&#xff1f; 在浏览器支持 ES 模块之前&#xff0c;JavaScript 并没有提供原生机制让开发者以模块化的方式进行开发。 缓慢的服务器启动 当冷启动开发服务器时&#xff0c;基于打包器的方式…

YOLOv8来啦!YOLO内卷期模型怎么选?9+款AI硬件如何快速部署?深度解析

在这新春佳节到来之际&#xff0c;回顾整个虎年&#xff0c;堪称YOLO内卷元年&#xff0c;各路YOLO系列神仙打架&#xff0c;各显神通。一开始大部分用户做项目做实验还是使用的YOLOv5&#xff0c;然后YOLOv6、YOLOv7、PP-YOLOE、DAMO-YOLO、RTMDet就接踵而至&#xff0c;于是就…

SpringBoot原理解析

目录 一、Profile功能 &#xff08;一&#xff09;、application-profile功能 &#xff08;二&#xff09;、Profile条件装配功能 &#xff08;三&#xff09;、profile分组 二、外部化配置 &#xff08;一&#xff09;、外部配置源 &#xff08;二&#xff09;、配置文…

消息队列 ---nsq

设计 topic和channel 单个nsqd实例旨在一次处理多个数据流。流称为“主题”&#xff0c;一个主题有 1 个或多个“通道”。每个通道都会收到一个主题的所有消息的_副本_。 主题和通道道_不是_提前配置的。主题是在首次使用时通过发布到指定主题或订阅指定主题的通道来创建的。…

如何看懂行业分析报告?

从下面几部分聊聊行业分析&#xff1a;1.什么时候需要做行业分析&#xff1f;2.如何做行业分析&#xff1f;3.案例学习4.在工作中如何应用&#xff1f;5.在生活中如何应用&#xff1f;1.什么时候需要做行业分析呢&#xff1f;当你在对自己进行职业规划的时候&#xff0c;会思考…

【SpringCloud15】SpringCloud Stream消息驱动

1.消息驱动概述 1.1 为什么要引入消息驱动 1.2 是什么 概述&#xff1a;屏蔽底层消息中间件的差异&#xff0c;降低切换成本&#xff0c;统一消息的编程模型 官网 Spring Cloud Stream是用于构建与共享消息传递系统连接的高度可伸缩的事件驱动微服务框架&#xff0c;该框架提…

一 、Qml开发之环境搭建

进入官网下载相应版本的qtcreator &#xff1a;https://download.qt.io/archive/qt/5.12/5.12.6/ 1.1 安装的时候注意如下对话框&#xff0c;需要选择下图所示的必须选项&#xff0c;因为我是mac 所以选择的macOS下载完之后进行点击安装&#xff0c;安装后运行软件图片如下&…

小程序uni-app介绍

uni-app介绍 uni-app简介 uni-app 是一个使用**Vue.js **开发所有前端应用的框架&#xff0c;开发者编写一套代码&#xff0c;可发布到iOS、Android、Web&#xff08;响应式&#xff09;、以及各种小程序&#xff08;微信/支付宝/百度/头条/QQ/钉钉/淘宝&#xff09;、快应用…

C++|读写xml文件开源库tingxml2的使用

参考&#xff1a; TinyXML使用方法[通俗易懂] https://cloud.tencent.com/developer/article/2037579 TinyXML2 入门教程&#xff08;这篇写很好&#xff0c;本文侧重讲解使用不过做多介绍&#xff09; 不了解xml的建议自行查阅&#xff0c;在此不赘述。 开源库github链接&…

Python中的列表、元组、字典

​​​​​​​列表是一种让程序员在代码中批量表示/保存数据的方式&#xff0c;元组和列表相比&#xff0c;是非常相似的&#xff0c;只是列表中放哪些元素可以修改调整&#xff0c;元组中放的元素是创建元组的时候就设定好的&#xff0c;不能修改调整。 列表和元组类似于其他…