怎做不下网站刷枪,宝塔网站301重定向怎么做,晋中做网站公司,像素点建网站Python中的垃圾回收#xff08;Garbage Collection#xff0c;简称GC#xff09;机制是一个自动内存管理过程#xff0c;它负责在对象不再被使用时释放内存资源。Python的垃圾回收主要依赖于引用计数#xff08;Reference Counting#xff09;来跟踪和回收不再使用的对象…Python中的垃圾回收Garbage Collection简称GC机制是一个自动内存管理过程它负责在对象不再被使用时释放内存资源。Python的垃圾回收主要依赖于引用计数Reference Counting来跟踪和回收不再使用的对象。除了引用计数Python的垃圾回收器还包括一个循环检测器用于检测并回收循环引用中涉及的对象。
对象的底层C语言结构体
在Python中我们知道一切皆对象那么在在CPython实现中CPython是Python的官方和最广泛使用的实现每个对象确实都是由一个C语言结构体表示的。包括数字、字符串、函数等。这些对象在底层都对应着一个C语言结构体——PyObject。
PyObject
PyObject 是所有Python对象共有的部分它定义了对象的引用计数和类型指针。引用计数用于垃圾回收而类型指针则指向一个表示该对象类型信息的结构体。
typedef struct _object {_PyObject_HEAD_EXTRAPy_ssize_t ob_refcnt;struct _typeobject *ob_type;
} PyObject;ob_refcnt 是一个引用计数器它帮助Python进行自动内存管理。ob_type 指向一个表示该对象所属类型的结构体。
PyVarObject
对于那些大小可变的数据类型如列表、字典等Python使用了另一种结构体——PyVarObject。这个结构体继承自 PyObject, 并添加了额外的成员来记录变量长度。
typedef struct {PyObject_VAR_HEADPy_ssize_t ob_size; /* Number of items in variable part */
} PyVarObject;ob_size 表示容器中元素数量。
类型特定结构体
除了通用部分之外每种具体数据类型还有自己特定信息需要存储。例如 对于整型(PyLongObject) typedef struct {PyObject_VAR_HEADdigit ob_digit[1];
} PyLongObject;对于字符串(PyUnicodeObject) typedef struct {// ... 其他成员 ...Py_ssize_t length; /* Length of raw Unicode data */wchar_t *data; /* Raw Unicode buffer */// ... 其他成员 ...
} PyUnicodeObject;每种数据类型都会有其专门设计的C语言结构来存储其特定信息和行为方法如整型数字具有不同大小和符号性质字符串则需要记录字符序列及长度等。通过这样丰富多样化地设计内部数据结构与算法实现细节Python能够以统一且高效率地方式处理各类数据操作与交互逻辑。
小结 ob_refcnt这是一个整数表示对象的引用计数。当有新的引用指向对象时这个计数器会增加当引用被删除时计数器会减少。当引用计数降到0时对象将被垃圾回收器回收。 ob_type这是一个指向PyTypeObject结构体的指针它包含了对象的类型信息。PyTypeObject结构体包含了与对象类型相关的数据如类型名称、大小、类型方法、基类等。 ob_size这是一个用于跟踪对象中元素数量的字段它主要用于序列类型如列表和元组。对于非序列类型的对象这个字段可能没有实际用途。 数据这是对象实际数据存储的地方。具体布局取决于对象的类型。例如对于整数对象数据可能直接存储为整数值而对于复合类型数据可能包括指向其他对象的指针。
引用计数
基本概念
引用计数是一种简单直观的内存管理技术它通过跟踪每个对象被引用的次数来决定对象是否可以被垃圾回收。引用计数是Python垃圾回收机制的核心。
通过上面的结构体我们知道每个对象都有一个引用计数用于记录有多少个引用指向该对象。当对象被创建时引用计数初始化为1。如果有新的引用指向该对象引用计数增加如果引用被删除引用计数减少。当对象的引用计数降为0时意味着没有任何引用指向该对象对象将立即被回收其占用的内存被释放。
创建对象时当你创建一个新对象时比如a 3Python会为这个对象分配内存并将其引用计数设置为1。增加引用如果有其他变量被赋值为这个对象例如b a该对象的引用计数会增加。减少引用如果对该对象的一个引用被销毁或者被重新赋值例如a 5则原来指向那个对象的引用计数会减少。回收内存一旦某个对象的引用计数变为0意味着没有任何变量或者数据结构再指向它Python就会自动释放掉这块内存。
引用计数机制不能自动处理循环引用的情况即两个或多个对象相互引用导致它们的引用计数永远不会降到0。为了解决这个问题CPython使用了一个循环垃圾收集器。这个收集器会定期运行通过一个标记-清除算法来识别和回收循环引用的对象。
查看引用计数
在Python中你可以通过标准库中的sys模块来查看和操作一个对象的实际参考次数。以下是一些例子
import sys# 创建一个列表
a []# 查看列表a 的当前参考次数
print(sys.getrefcount(a)) # 注意: 调getrefcount本身也会临时增加一次参考, 所以结果比预期多1# 创建另外两个别名指向同一列表
b a
c a# 再次检查参考次数量
print(sys.getrefcount(a)) # 现在应该是4了: a, b, c 和 getrefcount 的临时调用# 删除其中一个别名减少参考数量
del b
print(sys.getrefcount(a)) # 参考数量减少到3了: a, c 和 getrefcount 的临时调用
循环垃圾收集器Cycle-GC
为了解决引用计数无法处理循环引用的问题Python引入了循环垃圾收集器。这个收集器使用一种称为“标记-清除”Mark-Sweep的算法来检测并回收循环引用的对象。这个收集器主要针对容器对象如列表、字典、类实例等因为这些类型的对象更可能形成循环引用。
算法
循环垃圾收集器基于“标记-清除”Mark-Sweep算法
标记阶段从一组根对象开始如全局变量、活动栈帧等遍历所有可达对象。可达意味着从根对象出发可以通过某种方式访问到该对象。在遍历过程中每访问到一个对象就将其标记为活动的。注意这一阶段并不是将“标记”的对象放入某个容器中等待回收。清除阶段遍历所有对象将未标记的对象视为垃圾进行回收。未标记的对象即为那些在标记阶段没有从根对象可达的对象即不再被任何活动引用所引用。这一步骤并不涉及先存放在某个容器或列表中相反一旦确定为垃圾就会立刻进行资源回收处理。
触发时机
循环垃圾收集Cyclic Garbage Collector在Python中并不是以一个独立的监控线程的形式存在。它是Python内存管理机制的一部分特别是在CPython实现中它与主程序运行在同一个线程中。循环垃圾收集器会根据特定条件被触发执行例如当分配操作计数达到某个阈值时。
循环垃圾收集器通常在分配了一定数量新对象或者释放了一定数量旧对象之后自动触发。Python也提供了手动触发GC的接口gc.collect()允许开发者根据需要控制GC执行时间点。
分代回收Generational GC
Python的垃圾回收器还采用了分代回收策略将对象分为三代0代、1代和2代。新创建的对象属于0代如果它们经过一次垃圾回收仍然存活将被移动到1代再经过一次回收仍然存活的对象则被移动到2代。分代回收的思想是存活时间越长的对象被回收的可能性越小。因此垃圾回收器会更频繁地回收低代对象而较少地回收高代对象。
尽管引用计数很高效但它无法解决循环引用问题。例如两个对象相互引用即使它们已经不再使用了由于彼此持有对方的一个有效引用导致其引用计数永远不会降到0。为了解决这一问题在基于引用计数之上Python还采取了分代收集策略。
第0代Generation 0这里包含所有新创建的对象。由于许多对象会很快变得不可达因此第0代会频繁进行垃圾回收。第1代Generation 1当一个对象在第0代经历过一次垃圾回收后仍然存活它会被移动到第1代。相比于第0代这里的垃圾回收频率较低。第2代Generation 2类似地从第1代存活下来的对象会被移动到更稳定、更少进行垃圾回收操作的第2 。如果在第一代中进行了垃圾回收仍然存活下来则将其移动到第二代。同样地在第二代中存活下来后会移动到第三代。随着世代等级增加, 对象在内存中存在时间越长。分代算法假设生命周期短暂(如局部变量)或非常长久(如全局配置信息) 的数据较多, 因此通过调整各世代触发GC(Garbage Collection) 的阈值可以有效提高GC性能。当执行分代回收时, Python会检查较年轻一些世界里面是否存在循环应用并清除那些无法访问到达外部状态 (unreachable) 的循环。
分层次进行GC
通过将新创建和短命命期预期高、以及长寿命预期高但数量相对较少且稳定性强等特点区别开来处理和管理内存资源使用情况
在每一次GC执行时并非检查所有三个世界级别上所有容器型数据结构实例状态而是首先针对最年轻那一带即最可能出现大量无效引用关系链条需要清理掉以释放占用资源空间情况进行检查处理。只有当年轻带上执行了足够多次GC操作之后才考虑向上逐级提升检查范围至更老旧带级别并且随着带级别增加其检查频率也随之降低。
使用分层次方法可以显著减少必须要遍历和检查整体数据结构实例状态所需时间和计算资源消耗量——因为大部分新创建实例都可能很快就变成无效引用状态并需要清理掉同时保证那些确实需要长时间保持有效状态直至程序运行结束阶段才释放掉占用资源空间情况下能够尽可能减少干扰影响。
背后的数据结构
每一代generation都有自己的链表来跟踪和管理属于该代的所有对象。这些链表使得垃圾收集器能够有效地遍历特定代中的对象以执行垃圾回收。
在CPython中分代回收是通过以下几个关键数据结构来实现的
PyObject: 这是所有Python对象共有的基础结构体。它包含了引用计数和指向其类型描述符的指针。gc模块: Python提供了一个内置模块gc它暴露了与垃圾收集相关的功能和接口包括手动触发垃圾回收、调整阈值、查看各代对象列表等。Generation链表: 每一代都有自己独立的链表来跟踪该代中所有活动即未被回收对象。当进行垃圾回收时GC会根据这些链表遍历并检查每一代。每个代的gc_generation结构体还包含两个其他重要的字段count和threshold。count字段用于跟踪自上次垃圾回收以来分配和释放的对象数量。threshold字段是一个阈值当count超过这个阈值时就会触发该代的垃圾回收。
默认阈值
阈值分为三代Generation 0, 1, 2每一代都有自己的阈值。这些阈值可以通过gc.get_threshold()函数获取并且可以通过gc.set_threshold()函数进行设置。
第0代Generation 0的阈值通常设置为700。第1代Generation 1的阈值通常设置为0代 10次回收后。第2代Generation 2的阈值通常设置为1代 10次回收后。
调整阈值
开发者可以根据应用程序的需要调整这些阈值。例如如果你认为应用程序中的对象生命周期更长你可能希望减少垃圾回收的频率以避免性能开销。在这种情况下你可以增加阈值。相反如果你希望更积极地回收不再使用的对象你可以减少阈值。
import gc# 获取当前的阈值设置
thresholds gc.get_threshold()
print(当前的gc分代阈值分别是, thresholds)# 设置新的阈值
new_thresholds (600, 10, 5) # 分别为第0、1、2代的阈值 0代为对象个数1代和2代为对应前一代的gc次数
gc.set_threshold(*new_thresholds)
thresholds gc.get_threshold()
print(最新的gc分代阈值分别是, thresholds)GC优化
Python的内存管理和垃圾回收机制包括多种优化策略以提高性能和减少内存使用。其中对象池Object Pools和空闲列表Free Lists是两个重要的优化手段。这些技术主要用于管理小对象的分配和回收因为频繁地为小对象分配和释放内存会导致大量的性能开销。
对象池
前面的文章提到过https://blog.csdn.net/weixin_39743356/article/details/136077846
Python中最著名的对象池是针对小整数Small Integers和短字符串Short Strings的优化。 小整数池Python解释器启动时会创建一个范围在[-5, 256]之间的整数对象池。当程序需要这个范围内的任何整数时Python都会从这个池中返回相应的引用而不是新建一个对象。这样做可以避免频繁创建和销毁常用数字对象。 短字符串驻留对于一些内容相同且长度较短的字符串在程序运行期间只会创建一次并被重复使用。这种机制称为字符串驻留String Interning它可以帮助节省内存并加速字典类型键值对查找。
空闲列表
空闲列表是另一种优化技术主要用于快速分配与回收特定类型对象所占用的内存空间。 列表、字典、集合等容器类型当这些容器被销毁时它们占据的内存不会立即返回给操作系统而是保留在一个空闲列表中。下次再创建同类型容器时就可以直接重用这块内存区域。 元组: Python还维护了一个专门针对元组大小不同情况下可复用数据块状态记录表——每当有新元组需要创建且其大小符合某个已存在记录项所描述状态时则直接从该记录项关联空闲列表中取出一块数据区域进行初始化使用反之则将不再需要且大小符合某记录项描述状态得到旧元组数据区域归还至相关联空闲列表备份后续可能需求。
内部碎片处理
在Python中内部碎片Internal Fragmentation是指在内存分配过程中由于分配的内存块不能完全被使用而导致的内存空间浪费现象。这种情况通常发生在使用固定大小的内存块分配策略时例如当一个对象所需的内存大小不是内存块大小的整数倍时就会在分配的内存块中留下未使用的部分这就是内部碎片。
除了上述策略外CPython还通过“块”(block)管理来减少因小规模分配造成的外部碎片问题 PyMalloc是Python内存管理系统中的一个组件它负责处理对象的内存分配请求。PyMalloc的设计目的是为了优化小对象的内存分配减少内存碎片提高内存分配的效率。 在Python的内存管理中对象根据大小被分为不同的类别。PyMalloc主要负责分配小于某个阈值通常是256字节的小对象。它使用了一个称为“池”pool的数据结构来管理这些小对象的内存分配。这些池被组织成多个“块”block每个块包含多个大小相同的对象。 当Python代码创建一个小对象时PyMalloc会尝试在一个已有的块中找到一个足够大的空闲空间来放置这个对象。如果没有合适的空闲空间PyMalloc会分配一个新的块来满足请求。这种分配策略有助于减少内存碎片并提高内存分配的速度。