垃圾收集-引用计数机制
python采用的是引用计数机制为主,隔代回收机制为辅的策略
栈内存与堆内存
栈内存 存放变量名 里面有个变量可以指向堆内存的值位置(内存地址)
堆内存 对象(值)
在Python中,每个对象都有指向该对象的引用总数—引用计数
当使用某个引用作为参数, 传递给 getrefcount() 时, 参数实际上创建了一个临时的引用
因此, getrefcount() 所得到的结果, 会比期望的多1
import sys
class Test(object):
def __init__(self):
print('当前对象已创建, 占用内存地址 %s' % hex(id(self)))
a = Test()
print('当前对象的引用计数: ', sys.getrefcount(a))
引用计数增加
# 对象被创建
a = Test()
print('当前对象的引用计数: ', sys.getrefcount(a)) # 2
# 另外变量也指向当前对象
b = a
print('当前对象的引用计数: ', sys.getrefcount(a)) # 3
# 作为容器对象的一个元素
c = [a, b]
print('当前对象的引用计数: ', sys.getrefcount(a)) # 5
引用计数减少
# 变量被显式的销毁
# del a
print('当前对象的引用计数: ', sys.getrefcount(b)) # 4
# 对象的另外一个变量重新赋值 注意如果列表等存在该元素时并不会减少
b = 1000
print('当前对象的引用计数: ', sys.getrefcount(a)) # 3
# 从容器中移除
c.remove(a)
print('当前对象的引用计数: ', sys.getrefcount(a)) # 2
当引用计数为0时, 这个对象就会垃圾回收, 内存空间就被清空
垃圾收集-隔代回收机制
出现交叉引用(循环引用)就不能使用引用计数,
三个链表,零代链表上的对象(新创建的对象都加入到零代链表),引用数都是一,每增加一个指针,引用加一,随后
python会检测列表中的互相引用的对象,根据规则减掉其引用计数
gc模块
0号链表 出现 700个对象就会触发
1号链表 出现 10个对象就会触发
2号链表 出现 10个对象就会触发
import sys
class Test(object):
def __init__(self):
print('当前对象已创建, 占用内存地址 %s' % hex(id(self)))
def __del__(self): # 被回收之前会调用
print('当前对象马上被系统gc回收 内存地址 %s' % hex(id(self)))
a = Test()
b = Test()
# 以下情况出现交叉引用
a.pro = b
b.pro = a
del a
print(sys.getrefcount(b))
import sys
import time
import gc
def timePlus():
t = time.time()
return '%s %.4f' % (time.strftime('%H:%M:%S', time.localtime(t)), t - int(t))
class Test(object):
def __init__(self):
print('当前对象已创建, 占用内存地址 %s' % hex(id(self)))
def __del__(self): # 被回收之前会调用
print('当前对象马上被系统gc回收 内存地址 %s' % hex(id(self)))
while True:
a = Test()
b = Test()
# 以下情况出现交叉引用
a.pro = b
b.pro = a
del a
del b
print(gc.get_threshold(), timePlus()) # 打印隔代回收的阈值
print(gc.get_count()) # 打印gc需要回收的对象的数量
time.sleep(0.2)
gc.disable() # 禁用隔代回收 一般不要禁用