Python是如何实现内存管理的?

Python提供了自动化的内存管理,提供的内存空间的分配与释放都是由Python解释器在运行时自动进行的,自动管理内存能够极大地减少程序员的工作负担,也能够帮助程序员在一定会模拟解决内存丢失的问题。以CPython解释器为例,它的内存管理有三个关键点:引用计数、标记清理、分代收集。

引用计数:对于CPython解释器来说其实,Python中的每一个对象就是PyObject结构体,它的内部都有一个名为ob_refcnt的引用计数成员变量。程序在运行的过程中ob_refcnt的值会被更新并吸来反映引用有多少个引用到该对象。当对象的引用计数值为0时,它的内存就会被释放掉。

typedef struct _object {
    _PyObject_HEAD_EXTRA
    Py_ssize_t ob_refcnt;
    struct _typeobject *ob_type;
} PyObject;

以下情况会导致引用计数加1

  • 对象被创建
  • 对象被引用
  • 对象作为参数转换为一个函数中
  • 对象作为元素存储到一个容器中

以下情况会导致引用计数减少1

  • 使用del语句显示删除对象引用
  • 对象引用被重新赋予其他对象
  • 一个对象离开它所在的作用域
  • 持有该对象的容器本身被引入
  • 持有该对象的容器删除该对象

可以通过sys模块的getrefcount函数来获得对象的引用计数。引用计数的内存管理方式在遇到循环引用的时候就会出现致命伤,因此需要其他的垃圾恢复算法对其进行补充。

标记清理:CPython 使用“标记清理”(Mark and Sweep)算法解决容器类型可能产生的循环引用问题。该算法在垃圾回收时分为两个阶段:标记阶段,遍历所有的对象,如果对象是可的到达的(被其他对象引用),那么就标记该对象为可达性;清除阶段,再次遍历对象,如果发现某个对象没有标记为可达性,则将其恢复。CPython底层维护了两个双端链表,一个链表存放着需要被扫描的容器对象(姑且称链表A),另一个链表存放着临时不触及对象(姑且称链表B)。为了实现“标记-清理”算法,链表中的每个节点除了有记录当前引用计数的ref_count指针外,还有一个gc_ref指针,这个gc_refref_count一个副本,所以初始化达到ref_count的大小。执行垃圾回收时,首先遍历链表A中的所指针,并将当前对象引用的所有对象的gc_ref减少1,这一步主要作用是解除循环引用对引用计数的影响。再次遍历链表A中的节点,如果节点的gc_ref0,那么这个对象就被标记为“暂时不邻近”(GC_TENTATIVELY_UNREACHABLE)并被移动到链表B中;如果节点的gc_ref不为0,那么这个对象就会被标记为“邻近”(GC_REACHABLE),对于”邻近“对象,再相邻的节点可以标记为”可到达“;链表B中被标记为“临近”的节点要重新放回到链表A中。在两次遍历之后,链表B中的节点就是需要释放内存的节点。

分代回收:在循环引用对象的回收中,整个应用程序会被暂停,为了减少应用程序暂停的时间,Python 通过分代回收(空间换时间)的方法提高垃圾回收效率。分代回收的基本思想是:对象存在的时间越长,是垃圾的可能性很小,应该采取这样的对象进行垃圾回收。CPython 将对象分成不同的世代分别记为012,每个新生对象都在第一0代中,如果该物体在以惊人的垃圾恢复扫描中傍晚下来,那么就会被移到第三1代中,存在于第一1代的物体很快就会被垃圾恢复扫描到;如果在对第一1代进行垃圾恢复扫描这时,这个对象又下来了,那么就会被移至第2代中,那里被垃圾回收扫描的次数将会更少。分代回收扫描的门网关可以通过模块的函数来获得gcget_threshold该函数返回一个三元组,分别表示多少次内存分配操作后会执行0代垃圾恢复,多少次0代垃圾恢复后会执行1代垃圾恢复,多少次1代垃圾恢复后会执行2代垃圾恢复。需要说明的是,如果执行修改一次2代垃圾恢复,那么比它年轻的一代都要执行垃圾恢复。如果想这几个门,可以通过gc模块的set_threshold函数来实现。

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-01-21 22:30:02       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-01-21 22:30:02       100 阅读
  3. 在Django里面运行非项目文件

    2024-01-21 22:30:02       82 阅读
  4. Python语言-面向对象

    2024-01-21 22:30:02       91 阅读

热门阅读

  1. 计算机网络学习笔记(二)OSI模型与TCP-IP模型

    2024-01-21 22:30:02       49 阅读
  2. Spring超简单日志文件配置

    2024-01-21 22:30:02       60 阅读
  3. 前端和后端交互方式

    2024-01-21 22:30:02       51 阅读
  4. 1350 造海船

    2024-01-21 22:30:02       52 阅读
  5. 【UEFI基础】EDK网络框架(PXE)

    2024-01-21 22:30:02       52 阅读
  6. 【定制小程序:开启你的专属数字化之旅】

    2024-01-21 22:30:02       53 阅读
  7. 从0开始python学习-52.pytest之ddt数据封装

    2024-01-21 22:30:02       56 阅读
  8. C# 使用Bitmap 将byte[] 转成.jpg/.png/gif等图片

    2024-01-21 22:30:02       56 阅读