Python中的内存管理---引用计数与垃圾收集

1. CPython介绍

Python 是一种解释型编程语言。你的 Python 代码实际上被编译成更多计算机可读的指令,称为字节码 (即我们经常在 __pycache__ 文件夹中见到的 .pyc 文件)。当您运行代码时,这些指令由虚拟机解释

默认的 Python 实际上是CPython,  是用 C 编写的,运行时由它解释 Python 字节码。除 CPython 之外还有其他实现

常见python解释器
名称CPythonIronPythonJythonPyPy
语言CC#JavaPython

本文我只介绍由 Python 的默认实现 CPython 完成的内存管理.  在Python中, 一切都是对象, (甚至 int, str 这些数据类型也是对象), 但是C语言本身不支持 OOP (面向对象编程), CPython的实现中, 有一个 struct 叫做 PyObject,CPython 中的每个其他对象都使用它。

Python中的所有对象都可以理解为是 PyObject 的子类. PyObject只包含2个内容

  1. ob_refcnt : 引用计数
  2. ob_type: 指向实际对象类型的指针

ob_type 指向的类型, 是另一种 C struct, 它描述一个Python 对象 (例如 str 或 list). 每个对象都有一个内存分配器和释放器, 用于获取和释放内存.

2. 垃圾收集

前面提到, Python 中的每个对象都有一个引用计数和一个指向类型的指针。当对象的引用计数为 0 时, Python 解释器就会从内存中将该对象删除. 

2.1 引用计数的增加与减少

以下3种行为会使得对象的引用增加

  1. 被分配给一个变量
  2. 被作为参数传递
  3. 被包含在其他可变对象中, 如列表和字典

可以使用sys模块中的getrefcount方法检查对象的当前引用计数, 将对象作为参数传递给getrefcount方法会增加引用计数1

In [1]: import sys

In [2]: a = [1, 2]  # 列表[1, 2] 赋值给变量a, 引用计数为 1
In [3]: sys.getrefcount(a)  # a 作为参数传给函数, 引用计数+1, 为2
Out[3]: 2

In [4]: b = a # 列表[1, 2] 赋值给变量b, 引用计数+1 
In [5]: sys.getrefcount(a)
Out[5]: 3

In [6]: dct = {'key': a} # a 包含在字典lst中, 引用计数+1
In [7]: sys.getrefcount(a)
Out[7]: 4

 与引用计数增加的行为相反,  删除变量可以使引用计数减少

In [8]: del dct
In [9]: sys.getrefcount(a)
Out[9]: 3

In [10]: del b
In [11]: sys.getrefcount(a)   
Out[11]: 2   

# getrefcount函数将引用计数+1, 函数执行完毕后 a 的引用计数为 1
In [10]: del a  # a的引用计数为0, 内存中释放列表[1, 2]对象

3. 避免写入冲突--GIL

可以把计算机的内存理解为一个笔记本, 不同的进程就是不同的作者, 大家可以共同在笔记本中书写和擦掉自己的内容. 如果不同的作者都在同一页笔记本写数据, 整个页面相互叠加, 双方的内容都变得不可读.

CPython的解决方案是增加一个全局解释器锁 GIL (Global Interpreter Lock),  GIL 通过锁定整个解释器使得同一时刻, 只有一个线程可以拥有解释器锁, 避免了内存冲突. 可以把 GIL 理解为一跟公用的笔,  同一时刻只能有一个作者能申请使用笔来写入笔记. 

另一个原因是, 上文提到垃圾收集依赖于对象的引用计数. 引用计数变量需要避免被两个线程同时增加或减少其值. 例如如下代码, 此时列表 [1] 的引用计数为2.  

 b = a = [1]

若两个进程同时执行 del b del a , 2个进程获取的引用计数都是2, 将其 -1 之后, 引用计数还是1. 造成内存泄漏, 对象 [1] 永远停留在内存中, 直到Python解释器结束. 

总结

CPython中, 对所有对象维护一个引用计数, 一旦下降到0, 将对象从内存中删除.

CPython依赖全局解释器锁来避免内存冲突.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值