python垃圾回收机制与很多_高级编程技巧——Python垃圾收集和性能分析,回收,及...

高级编程技巧——Python 垃圾回收及性能分析

Python 垃圾回收及性能分析

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

我们有三个图形 Circle,Triangle,Rectange

他们都有一个获取面积的方法,但是方法名字不同,我们可以实现一个统一的获取面积的函数,使用每种方法名进行尝试,调用相应的接口。

class Triangle:

def __init__(self,a,b,c):

self.a,self.b,self.c=a,b,c

def get_area(self):

a,b,c= self.a,self.b,self.c

p=(a+b+c)/2

return (p*(p-a)*(p-b)*(p-c))**0.5

class Rectangle:

def __init__(self,a,b):

self.a, self.b=a,b

def getArea(self):

return self.a *self.b

class Circle

def __init__(self,r)

self.r=r

def area(self):

return self.r **2 ** 3.14159

getattr(x,‘y’,None)

—>等同于 x.y 当中不含有y时,返回None。

map(func,iterable)

—> 将iterable 中的元素一一映射到func函数中处理,并且返回新的map对象。

3.2 垃圾回收机制

介绍

在Python 程序运行的时候,会在内存中开辟一块空间,用于存放临时变量;当计算完成之后,就会将结果输出到永久存储器中,如果数据量特别大,那内存空间管理部妥当的话就非常容易爆内存,程序可能直接终止。

在Python中,一切皆对象。所以,每一个变量,实际上都是对象的一个指针。所以,当这个对象的引用计数(指针数)为0的时候,说明它也变成了垃圾,需要被放到回收箱中。

OS模块

与操作系统交互的库

psutill

模块

与系统交互的库,能够轻松实现获取系统运行的进程和系统利用率(包括CPU、内存、磁盘、网络等)信息,它是用来做系统监控、性能分析、进程管理。

import os

import psutil

def show_info(start):

# 获取当前进程id

pid=os.getpid()

# 获取当前堆成对象

p=psutil.Process(pid)

# 获取进程独自占用的物理内存 换算单位MB

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(1000000)]

show_info('created')

func()

show_info('finished')

import os

import psutil

def show_info(start):

# 获取当前进程id

pid=os.getpid()

# 获取当前堆成对象

p=psutil.Process(pid)

# 获取进程独自占用的物理内存 换算单位MB

info=p.memory_full_info()

memory=info.uss/1024./1024

print(f'{start}一共占用{memory:2f}MB')

def func():

show_info('initial')

global a # a是局部变量,用global 声明它是全局变量

a=[i for i in range(1000000)]

show_info('created')

func()

show_info('finished')

import psutil

def show_info(start):

# 获取当前进程id

pid=os.getpid()

# 获取当前堆成对象

p=psutil.Process(pid)

# 获取进程独自占用的物理内存 换算单位MB

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(1000000)]

show_info('created')

return a

a=func()

show_info('finished')

a是局部变量

时,在返回到函数调用处时,局部变量的引用会注销。这时,列表a所指代对象的引用数为0,Python便会执行垃圾回收,因此之前占用的内存被收回了。

a是全局变量

时,即使函数体内代执行完毕,返回到函数调用处时,对列表a的引用仍然是存在的,所以对象不会被垃圾回收,仍然占用大量内存。

Python 内部的引用计数机制

可以通过sys.getrefcount() 这个函数,来了解**Python内部的引用计数机制。

import sys

a=[1,2,3]

# print(sys.getrefcount(a)) # a这里引用了 2 次

def func(a):

# 这里调用a 4次

# a本身一次 函数调用1次 函数参数一次 getrefcount 一次

print(sys.getrefcount(a))

func(a)

import sys

a=[1,2,3]

def func(a):

print(sys.getrefcount(a))

func(a)

# a作为形参相当于函数体内的临时变量 所以 调用执行完毕会被释放掉 引用次数为0

# 2 a 本身一次 getrefcount 一次

print(sys.getrefcount(a))

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所在内存地址

getrefcount 本身也会引入一次计数

手动启动垃圾回收

如果我们可以在

手动删除完对象的引用

,然后

强调用gc.collect()清除没有引用的对象

,其实也就是手动的启动对象的回收。

import sys

import gc

a = [1,2,3]

print(sys.getrefcount(a))

# a=None # 相当于将a 变量指向了 None

del a # 相当于自己把对象的引用删掉 本质上对象还没有被删除

gc.collect() # 手动启动回收

print(a) # NameError: name 'a' is not defined 此时a 已经被回收

import sys

import gc

a = 1 # 小整数对象池

b=10000

print(sys.getrefcount(a)) # 134

print(sys.getrefcount(b)) # 4

循环引用

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

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

b=[i for i in range(1000000)]

show_info('created')

# 相互引用

a.append(b)

b.append(a)

a=func()

gc.collect() # 手动回收 如果引用次数不为0时 手动回收 也是可以的

show_info('finished')

总而言之,当双向引用的时候,引用计数虽然还在,但我们可以手动拉起来回收,进行释放内存,所以,引用次数是垃圾回收的

充分非必要条件

调试内存泄漏

在Python 中通过引用计数和垃圾回收来管理内存,但是在一定情况下也会产生内存泄露

第一是对象被另一个生命周期特别长得对象所引用

第二是循环引用中的对象定义了 __del __函数

objgraph

,一个非常好用的可视化引用关系的包。在这个包中的 show_refs(),它可以清晰的引用关系图。

import objgraph

a=[1,2,3]

b=[4,5,6]

a.append(b)

b.append(a)

objgraph.show_refs(a)

.dot 文件转图片:https://onlineconvertfree.com/

ce9b09cbc579f9ce8fa14e0b0801caa5.png

3.3 用 pdb 进行代码调试

链接

链接

打印大法

Debug

pdb

如何使用pdb

首先,要启动pdb调试,我们只需要在程序中,加入 import pdb和pdb.set_trace() 这两行代码就行。

a=1

b=2

import pdb

pdb.set_trace

c=3

print(a+b+c)

这时,我们就可以执行,在IDE 断点调用器中可以执行的一切操作,比如打印,语法是’p’:

(pdb) p a

1

(pdb) p b

2

除了打印,常见的操作还有 ‘n’ ,表示继续执行代码到下一行

(pdb)n

-->print(a+b+c)

而命令!,则表示列举出当前代码行上下的11行源代码,方便开发者熟悉当前断点周围的代码状态

(pdb)1

a=1

b=2

import pdb

pdb.set_trace()

-> c=3

print(a+b+c)

命令 ‘s’ ,就是step into 的意思,即进入相应对应的代码内部

当然,除了这些常用命令,还有许多其它的命令可以调用

参考对应的官方文档:https//docs.python.org/3/library/pdb.htm|#module-pdb)

3.4 用cProfile 进行性能分析

除了要对程序进行调试,性能分析也是每个开发者的必备技能。

日常工作中,我们常常会遇到这样的问题:在线上,我发现产品的某个功能模块效率低下,延迟高,占用资源多,但却不知道是哪里出了问题。这时,对代码进行profile 就显得异常重要了。

这里所谓的profile ,是指对代码的每个部分进行动态的分析,比如准确计算出每个模块消耗的时间等。

计算斐波拉契数列,运用递归思想

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)

# 接下来 我想要测试一下这段代码总的效率以及各个部分的效率

import cProfile

cProfile.run('fib_seq(30)') # fib_seq(30) 必须为字符串

6c42b90dac68fe737ce6816cb14474a1.png

参数介绍

ncalls: 函数调用的次数,如果这一有两个值,就表示有递归调用,第二个值是原生调用次数,第一个值是总调用次数。

tottime: 函数内部消耗的总时间。(可以帮助优化)

percall: 是tottime 除以ncalls,一个函数每次调用平均消耗时间

cumtime:之前所有子函数消费时间的累积和。

filename: lineno(function): 被分析函数所在文件名,行号,函数名。

3.5 经典的参数错误

def add(a,d):

a+=b

return a

a=1

b=2

c=add(a,b)

print(c) # 3

print(a,b) # 1 2

def add(a,d):

a+=b

return a

a=[1,2]

b=[3,4]

c=add(a,b)

print(c) # [1, 2, 3, 4]

print(a,b) # [1, 2, 3, 4] [3, 4] 可变的数据类型

注意

列表为可变类型

li+=1 相当于改变li本身

li =li+1 相当于li 是两个变量 id 不一致 返回的是 原本的li

元组为不可变类型

tu +=1 也就是重新创建了一个 tu 变量 id 不一致。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值