Python学习3——回收机制和性能分析

在python中,一切皆对象,那么垃圾回收机制,其实就是当对象的引用计数为0,也就是当对象没有被调用的时候会被回收。(弱引用不占用引用计数)
python的回收机制一般是 计数回收为主,当遇到双向调用可使用标记清除和分代回收两种机制。

局部变量结束就会被回收

import os
import psutil

def show_info(start):
    # 获取当前进程id
    pid = os.getpid()

    # 获取当前堆成对象
    p = psutil.Process(pid)

    #  返回该对象的内存消耗
    info = p.memory_full_info()
    # 获取进程独自占用的物理内存 换算单位 mb
    memory = info.uss/1024/1024
    print(f"{start}一共占用了{memory:.2f}MB")


def func():
    show_info("initial")
    # a是局部变量 局部变量在函数执行完毕 引用就会注销
    a = [i for i in range(1000000)]
    show_info("created")

func()

show_info("finished")

在这里插入图片描述
这里的a是局部变量,所以结束就会被回收。
当a为全局变量的时候 global a,并没有回收
在这里插入图片描述

gc 手动垃圾回收

当出现双向引用到时候,引用计数始终存在,永远不为0,始终占用内存,那么它该不该被回收?这时候就要导入gc机制,手动进行垃圾回收

import gc
def show_info(start):
    pid = os.getpid()
    p = psutil.Process(pid)
    info = p.memory_full_info()
    memory = info.uss/1024./1024
    print(f"{start}一共占用{memory:.2f}MB")


def func():
    show_info("initial")
    a = [i for i in range(100000)]
    b = [i for i in range(100000)]
    show_info("after a,b created")

    # 相互引用
    a.append(b)
    b.append(a)

func()
gc.collect()
show_info("finished")

当双向引用的时候,引用计数虽然还在,但我们可以手动拉起回收,进行释放内存gc.collect()

sys.getrefcount()引用计数机制

导入sys,使用 sys.getrefcount(a)来获取a的引用次数,
sys.getrefcount本身会引入一次计数,多个sys.getrefcount(a) 只计一次
下图第一个Print输出的是2,一次是a 一次是sys.gerrefcount(a)
第二个Print输出的是4,一次a ,一次fun(a),以及形参调用的fun(a),还有一次sys.getrefcount(a)
在这里插入图片描述
第三次的Print输出是2,因为fun(a),这里的a作为形参相当于函数体内的临时变量,所以中间的不计次数,调用执行完毕会释放掉,到最后一行输出的只有两次,a本身一次,getrefcount一次
在这里插入图片描述
在这里插入图片描述

可视化关系 objgraph

可以通过导入objgraph来生成引用关系图

import objgraph 
#可视化引用关系的包  show_Refs 生成引用关系图

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

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

objgraph.show_refs(a)

pdb代码调试

因为debug只能往下执行,只能调试Python,所以在多种语言交互的情况下,调试很不方便,导入pdb,可以更好的调试代码。

import pdb
a = 1
b = 2
pdb.set_trace() # z追踪
c = 3
print(a+b+c)

def func():
    print(" i am func")

func()

pdb调试状态下,n:下一条语句,s:进入函数 h:帮助 l:列出附近的代码 p 输出值 q 退出debug

cprofile 性能分析

import cProfile
# 性能调试

def fib(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        return fib(n-1)+fib(n-2)
def fib_seq(n):
    res = []
    if n>0:
        res.extend(fib_seq(n-1))
    res.append(fib(n))
    return res

fib_seq(30)
cProfile.run('fib_seq(30)')

在这里插入图片描述
第一行:7049218个函数调用被监控,其中96个为原生调用
calls:被调用的次数,如果是两个值,表示有递归调用,第一个是总调用次数,第二个是原生调用次数
tottime:函数内部消耗的总时间,可以帮助优化
percall:tottime/calls 表示一个函数每次调用平均消耗时间
cumtime:之前所有子函数消费时间的累计和
filename:lineno:被分析函数所在文件名,所在的行号,函数名

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值