第三讲 垃圾回收及性能测试

通过实例方法名字的字符串调用方法

书写GetArea类

from math import pi
class GetArea:
	def __init__(self):
		pass
	def triangle(self, a, b, c):
		p = (a + b + c) / 2
		return (p * (p - a) * (p - b) * (p - c)) ** 0.5
	def rectangle(self, w, h):
		return w * h
	def circle(self, r):
		return pi * r ** 2

实例化这个GetArea类

getarea = GetArea()

如果我们只有字符串,如何直接调用该字符串对应的变量呢?这时候就要使用getattr函数了

attribute = 'triangle'
f = getattr(getarea, attribute, None)(3, 4, 5)
print(f) # 6
# None的含义是当shape不含有area属性的时候,返回None而不会报错

运用map函数可以轻松进行批量处理

attribute = 'circle'
r_list = [1, 2, 3]
area = map(getattr(getarea, attribute, None), r_list)
print(list(area))
# [3.141592653589793, 12.566370614359172, 28.274333882308138]

垃圾回收

获取当前进程的内存消耗需要导入os,psutil模块,定义show_info函数进行封装

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")

一个简单的应用

show_info("initial")
a = [i for i in range(1000000)]   # 列表推导式 [0~9999999]
show_info("created")
# initial一共占用20.43MB
# created一共占用59.17MB

对于一个函数中的局部变量,如果没有返回到外部,引用完了之后就会被删去该变量,变量占用的内存也会被回收

def func():
    show_info("initial")
    # 因为a是局部变量 局部变量在函数执行完毕 引用就会注销
    a = [i for i in range(1000000)]   # 列表推导式 [0~9999999]
    show_info("created")


func()
show_info("finished")
# initial一共占用20.41MB
# created一共占用59.17MB
# finished一共占用20.77MB

对于一个函数中的全局变量,引用完了之后不会被删去,变量占用的内存也不会被回收

def func():
    show_info("initial")
    # a为全局变量的时候 即使函数体内代码执行完毕 返回到函数调用处时,对列表a的引用仍然时存在的
    # 所以对象不会被垃圾回收,依然占有大量内存
    global a
    a = [i for i in range(1000000)]   # 列表推导式 [0~9999999]
    show_info("created")


func()
show_info("finished")
# initial一共占用20.46MB
# created一共占用59.28MB
# finished一共占用59.28MB

对于一个函数中的局部变量返回出去并被其他变量接受之后,该变量不会被删去,变量占用的内存也不会被回收

def func():
    show_info("initial")

    a = [i for i in range(1000000)]   # 列表推导式 [0~9999999]
    show_info("created")
    return a


a = func()
show_info("finished")
# initial一共占用20.46MB
# created一共占用59.26MB
# finished一共占用20.85MB

引用次数

sys.getrefcount用于统计一个变量的引用次数,即变量地址的访问次数

import sys

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

b = a
print(sys.getrefcount(a))   # 3次

c = b
d = c
print(sys.getrefcount(a))   # 5次

"""
1.getrefcount()只计一次引用次数 
2.变量赋值 b变量指向了a所在内存地址
"""

通过sys.getrefcount,我们就可以知道当一个变量被一个函数应用时的内存地址访问次数

a = [1,2,3]

def func(a):
    # 4次:a本身一次 函数调用一次 函数参数一次 getrefcount一次
    print(sys.getrefcount(a))

func(a) # 4次

手动启动回收

运用gc.collect()可以没有被引用的内存进行回收

循环引用

import os
import psutil
import gc
"""
引用次数 为0 的时候 一定会启用 垃圾回收吗
垃圾回收 一定是引用次数 为0的时候吗

充分非必要条件
"""
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 = [i for i in range(1000000)]   # 列表推导式 [0~9999999]
    b = [i for i in range(1000000)]  # 列表推导式 [0~9999999]
    show_info("created")

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


a = func()
gc.collect()            # 手动回收  如果引用次数 不为0时  手动回收 也是可以的
show_info("finished")
# initial一共占用20.47MB
# created一共占用97.88MB
# finished一共占用20.86MB

其实这个我还是不太理解

查看泄露

objgraph.show_refs查看内存的分配情况
上代码

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

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

objgraph.show_refs(a)
objgraph.show_refs(b)

生成关于a,b的内存分布情况,并保存为C:\Users\ADMINI~1\AppData\Local\Temp\objgraph-wkzkjd85.png,
C:\Users\ADMINI~1\AppData\Local\Temp\objgraph-das8cpbd.png
这2张图
a的内存分布
在这里插入图片描述
b的内存分布
在这里插入图片描述
很明显,b无法再循环引用自己

调试模式

使用模块pdb,在python文件中输入pdb.set_trace()就可以调试了

性能了解

使用模块cProfile,就可以了解每一个函数运行花费的时间

经典参数需注意的

列表是可变数据类型,元组是不可变数据类型
列表增添、删减元素,不会改变内存地址
元组增添、删减元素,会改变内存地址
特别要注意的是,如果对一个函数中输入一个列表,并对列表的元素进行改动,那么即使接下来不输出这个列表,这个列表的元素也会有不可逆的改变

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值