浅析Python垃圾回收机制!

Python垃圾回收机制

1. 内存泄露

  • 程序本身没有设计好,导致程序未能释放已不再使用的内存
  • 代码在分配了某段内存后,因为设计错误,失去了对这段内存的控制,从而造成了内存的浪费

监控python程序内存占用情况,psutil库

import os 
import psutil

# 显示当前程序占用内存大小
def show_memory_info(hint):
    pid = os.getpid() # 获取当前进程号
    p = psutil.Process(pid)
    info = p.memory_info()
    memory = info.rss / 1024. / 1024
    print(f"{hint} 内存用了:{memory}MB")

查看对象占用内存大小

import sys
a = [i for i in range(10000)]
memory = sys.getsizeof(a) / 1024
print(f"内存用了:{memory}KB")
# 内存用了:85.578125KB

2. Python什么时候启动垃圾回收机制?

2.1 计数引用

python中一切皆对象,所看到的一切变量,本质上都是对象的一个指针,当这个对象的引用次数为0的时候,说明这个对象永不可达,成为需要被回收的垃圾

# 查看引用次数
import sys
a = []
print(sys.getrefcount(a)) # 两次引用,一次a,一次getr
def func(a):
    # 四次引用,函数调用会产生两次额外的引用,一次来自函数栈,另一个是函数参数
    print(sys.getrefcount(a))
func(a)
2
4
def func():
    show_memory_info('初始')
    a = [i for i in range(1000000)] # 列表生成式
    show_memory_info('创建之后')
    
func()
show_memory_info('结束之后') # 内存即可被释放
初始 内存用了:52.4609375MB
创建之后 内存用了:91.921875MB
结束之后 内存用了:53.80859375MB
2.2 循环引用
  • python中使用标记清除算法和分代收集,来启动针对循环引用的自动垃圾回收
  • 标记清除算法,图论中的不可达概念
  • 分代收集算法中每一代都有一个默认阈值,超过指定阈值之后就会启动垃圾回收,如果垃圾回收启动太频繁,会造成程序性能低下,分代收集为了提高性能,因此不立刻回收。
def func():
    show_memory_info('初始')
    a = [i for i in range(1000000)] # 列表生成式
    b = [i for i in range(1000000)] # 列表生成式
    show_memory_info('创建之后')
    a.append(b)
    b.append(a)

func()
show_memory_info('结束之后') # 可以看到循环引用之后,内存依旧被占用
初始 内存用了:77.125MB
创建之后 内存用了:163.8828125MB
结束之后 内存用了:163.8828125MB

显示调用gc.collect()来启动垃圾回收

import gc

def func():
    show_memory_info('初始')
    a = [i for i in range(1000000)] # 列表生成式
    b = [i for i in range(1000000)] # 列表生成式
    show_memory_info('创建之后')
    a.append(b)
    b.append(a)

func()
# 显示调用gc.collect()来启动垃圾回收
gc.collect()
show_memory_info('结束之后') 
初始 内存用了:77.609375MB
创建之后 内存用了:145.92578125MB
结束之后 内存用了:77.609375MB
问题:引用计数是0是启动垃圾回收的充要条件吗?

引用计数是其中最简单的实现,不是充要条件,只能算作充分非必要条件,循环引用需要通过不可达判定,来确定是否可以回收。python中自动回收算法包括标记清除算法和分代收集。

原文地址:https://www.cnblogs.com/donghe123/p/13275183.html