第3章 Python垃圾回收

Python 垃圾回收机制
计数引用, Python 中一切皆对象。因此,你所看到的一切变量,本质上都是对象的一个指针,计数引用就是这个指针。

那么,怎么知道一个对象,是否永远都不能被调用了呢?

就是当这个对象的引用计数(指针数)为 0 的时候,说明这个对象永不可达,自然它也就成为了垃圾,需要被回收。

引用计数函数: sys.getrefcount()

import sys

a = [1, 2, 3, 4]  #第一次引用
b = a             #第二次引用
c = b             #第三次引用
print(sys.getrefcount(a))   #第四次引用

程序返回“a”的引用计数值为:4

1、计算当前内存使用量


import sys, os
import psutil
import gc

#计算内存使用量的函数
def show_memory_info(hint):
    pid = os.getpid()
    p = psutil.Process(pid)

    info = p.memory_full_info()
    memory = info.uss / 1024 / 1024
    print('{} memory used:{}MB'.format(hint, memory))


def func():
    show_memory_info('initial')
    #global a
    a = [i for i in range(10000000)]
    show_memory_info('after a, b created')

func()
gc.collect()
show_memory_info('finished')

上面程序返回值:
initia memory used:6.3359375MB
after a created memory used:393.734375MB
finished memory used:6.66796875MB
注意:
当用 global关键字 声明a为全局变量时,func函数低啊用结束时,内存不会释放。
当用一个变量接收func函数的 retuen a 时,内存也不会被释放。

2、循环引用

2.1 引用计数

import sys

a = [1,2,3]
b = a
print(sys.getrefcount(a))
print(sys.getrefcount(a))

返回值:3
说明:上面程序,a被引用了3次,print(sys.getrefcount(a)),相同的引用被当做一次处理。
第一次引用:a = [1,2,3]
第二次引用:b = a
第三次引用:print(sys.getrefcount(a))、print(sys.getrefcount(a))

2.2 循环引用

如果有两个对象,它们互相引用,并且不再被别的对象所引用,那么它们应该被垃圾回收吗?

import os
import psutil
import gc
#sys.getrefcount() 引用计数函数
# a = [1,2,3]
# b = a
# print(sys.getrefcount(a))

def show_memory_info(hint):
    pid = os.getpid()
    p = psutil.Process(pid)

    info = p.memory_full_info()
    memory = info.uss /1024 /1024
    print('{} memory used:{}MB'.format(hint,memory))

def func():
    show_memory_info('initial')
    a = [i for i in range(10000000)]
    b = [i for i in range(10000000)]
    show_memory_info('after a, b created')
    a.append(b)
    b.append(a)

func()
gc.collect() #func函数结束后,由于局部变量 a 和 b 互相引用,内存需要通过#gc.collect()回收
show_memory_info('finished')

1、上面程序如果注释掉gc.collect(),内存不会释放,使用情况如下:
initial memory used:8.4375MB
after a, b created memory used:783.7265625MB
finished memory used:783.7265625MB

2、上面程序如果用gc.collect()回收,则内存会被释放,使用情况如下:
initial memory used:8.7109375MB
after a, b created memory used:784.078125MB
finished memory used:9.54296875MB

2.3调试内存泄漏

虽然有了自动回收机制,但这也不是万能的,难免还是会有漏网之鱼。内存泄漏是我们不想见到的,而且还会严重影响性能。有没有什么好的调试手段呢?

它就是 objgraph,一个非常好用的可视化引用关系的包。在这个包中,我主要推荐两个函数,第一个是 show_refs(),它可以生成清晰的引用关系图。

下面代码在ipython执行,文件会自动保存在一个路径。例如:Graph written to C:\Users\Administrator\AppData\Local\Temp\objgraph-80cerxvn.dot (8 nodes)
Spawning graph viewer (xdot)
import objgraph

a = [1, 2, 3]
b = [4, 5, 6]

a.append(b)
b.append(a)

objgraph.show_refs([a])

在这里插入图片描述
dot转图片网站:https://onlineconvertfree.com/zh/
导入上面生成的文件后,会转换成图片,并提供下载。

总结

垃圾回收是 Python 自带的机制,用于自动释放不会再用到的内存空间;
引用计数是其中最简单的实现,不过切记,这只是充分非必要条件,因为循环引用需要通过不可达判定,来确定是否可以回收;
Python 的自动回收算法包括标记清除和分代收集,主要针对的是循环引用的垃圾收集;
调试内存泄漏方面, objgraph 是很好的可视化分析工具。

发布了31 篇原创文章 · 获赞 0 · 访问量 348

猜你喜欢

转载自blog.csdn.net/weixin_38027481/article/details/103794401
今日推荐