# 引用计数

Python垃圾回收 主要以 引用计数 为主, 分代回收 为辅。在 Python 中每一个对象的核心就是一个结构体 PyObject ,它的内部有一个 引用计数器(ob_refcnt) 。程序在运行的过程中会实时的更新 ob_refcnt 的值,来反映引用当前对象的名称数量。当某对象的引用计数值为 0 , 那么它的内存就会被立即释放掉。

  1. 以下情况是导致引用计数加一的情况:
  • 对象被创建,例如 a=2
  • 对象被引用,b=a
  • 对象被作为参数,传入到一个函数中
  • 对象作为一个元素,存储在容器中
  1. 下面的情况则会导致引用计数减一:
  • 对象别名被显示销毁 del
  • 对象别名被赋予新的对象
  • 一个对象离开他的作用域
  • 对象所在的容器被销毁或者是从容器中删除对象
  1. 通过 getrefcount() 来获取引用的对象当前的引用计数
import sys
class  Person:				# 对象被创建 +1
    pass
p1=Person()				# 实例化 +1
p2=p1					# 赋值 +1
print(sys.getrefcount(p1))
# 当前引用计数为 3
  1. 上面 p1 虽然赋值给了 p2 但是不会开辟新的内存空间,通过 id() 来进行查看
import sys
class  Person:
    pass
p1=Person()
p2=p1
print(id(p1))			# 打印结果:1279802481016
print(id(p2))			# 打印结果:1279802481016

# 标记清除解决循环引用

Python 采用了 “标记-清除”(Mark and Sweep)算法 ,解决容器对象可能产生的循环引用问题。

注意,只有 容器对象 才会产生循环引用的情况,比如 列表字典用户自定义类的对象元组 等。而像 数字字符串 这类简单类型不会出现循环引用。作为一种优化策略,对于只包含简单类型的元组也不在标记清除算法的考虑之列

import objgraph
import gc
# 循环引用
class Person:
    pass
class Dog:
    pass
p = Person()
d = Dog()
p.pet = d
d.master = p
del p
del d
gc.collect() #手动触发垃圾回收
print(objgraph.count("Person"))	# 打印结果:0
print(objgraph.count("Dog"))		# 打印结果:0
'''
对象间互相引用,导致对象不能通过引用计数器进行销毁
手动触发垃圾回收,挥手循环引用
'''

# 分代回收

在循环引用对象的回收中,整个应用程序会被暂停,为了减少应用程序暂停的时间,Python 通过 分代回收(Generational Collection)空间时间 的方法提高垃圾回收效率。

分代回收是基于这样的一个统计事实,对于程序,存在一定比例的内存块的生存周期比较短;而剩下的内存块,生存周期会比较长,甚至会从程序开始一直持续到程序结束。生存期较短对象的比例通常在 80%~90% 之间,这种思想简单点说就是:对象存在时间越长,越可能不是垃圾,应该越少去收集。这样在执行标记 - 清除算法时可以有效减小遍历的对象数,从而提高垃圾回收的速度。

python gc 给对象定义了三种世代 (0,1,2), 每一个新生对象在 generation zero 中,如果它在一轮 gc 扫描中活了下来,那么它将被移至 generation one , 在那里他将较少的被扫描,如果它又活过了一轮 gc, 它又将被移至 generation two ,在那里它被扫描的次数将会更少。

更新于

请我喝[茶]~( ̄▽ ̄)~*

Daniel ✨ 微信支付

微信支付

Daniel ✨ 支付宝

支付宝