Python高级编程技巧第3讲 - 垃圾回收机制、调试和性能分析、经典的参数错误

一、垃圾回收机制

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

import os
import psutil

# 显示当前 python 程序占用的内存大小
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)]
    show_memory_info('after a created')

func()
show_memory_info('finished')

引用计数:

'''
引用计数
'''

import sys

import gc
# 清除没有引用的对象
# gc.collect()

a = []


# 2 查看变量的引用次数  本身也要算一次
# print(sys.getrefcount(a))       # 2


def func(a):
    # 1 a = []
    # 2 函数调用
    # 3 函数的参数
    # 4 getrefcount
    print(sys.getrefcount(a))   # 4

func(a)

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


# 引用次数为0 是垃圾回收启动的充分必要条件嘛?

# import objgraph

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

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

# objgraph.show_refs([a])

# F8

1.1 循环引用

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

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()
show_memory_info('finished')

2.2 调试内存泄漏

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

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

     import objgraph
     
     a = [1, 2, 3]
     b = [4, 5, 6]
     
     a.append(b)
     b.append(a)
     
     objgraph.show_refs([a])
    

在这里插入图片描述
dot转图片:添加链接描述

总结

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

二、调试和性能分析

2.1 用 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)
    
  • 而命令l,则表示列举出当前代码行上下的 11 行源代码,方便开发者熟悉当前断点周围的代码状态

     (pdb) l
       1    a = 1
       2    b = 2
       3    import pdb
       4    pdb.set_trace()
       5  ->  c = 3
       6    print(a + b + c)
    
  • 命令“s“,就是 step into 的意思,即进入相对应的代码内部

  • 当然,除了这些常用命令,还有许多其他的命令可以使用

    参考对应的官方文档:https://docs.python.org/3/library/pdb.html#module-pdb%EF%BC%89

2.2 用 cProfile 进行性能分析

  • 日常工作中,我们常常会遇到这样的问题:在线上,我发现产品的某个功能模块效率低下,延迟高,占用的资源多,但却不知道是哪里出了问题。
  • 这里所谓的 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)')

在这里插入图片描述
参数介绍:

  • ncalls,是指相应代码 / 函数被调用的次数
  • tottime,是指对应代码 / 函数总共执行所需要的时间(注意,并不包括它调用的其他代码 / 函数的执行时间)
  • tottime percall,就是上述两者相除的结果,也就是tottime / ncalls
  • cumtime,则是指对应代码 / 函数总共执行所需要的时间,这里包括了它调用的其他代码 / 函数的执行时间
  • cumtime percall,则是 cumtime 和 ncalls 相除的平均结果。

三、经典的参数错误

3.1 小整数池:

  1. 在交互式模式下

范围: -5~256 之间
小整数池目的:节省内存,提高执行效率

  • Python为了优化速度,使用了小整数对象池, 避免为整数频繁申请和销毁内存空间。Python 对小整数的定义是 [-5, 256] 这些整数对象是提前建立好的,不会被垃圾回收。
"""
列表
"""
def add(a, b):
    a += b
    return a

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

c = add(a, b)

print(c)                        # [1, 2, 3, 4]
print('a={},b={}'.format(a, b)) # a=[1, 2, 3, 4],b=[3, 4]
"""
元组
"""
def add(a, b):
    a += b
    return a

a = (1, 2)
b = (3, 4)

c = add(a, b)

print(c)                             # (1, 2, 3, 4)
print('a = {},b = {}'.format(a, b))  # a=(1, 2),b=(3, 4)
  • 不可变类型

以int类型为例:实际上 i += 1 并不是真的在原有的int对象上+1,而是重新创建一个value为6的int对象,i引用自这个新的对象。

  • 可变类型

以list为例。list在append之后,还是指向同个内存地址,因为list是可变类型,可以在原处修改。

注意:在pycharm中运行python程序,pycharm出于对性能的考虑,会扩大小整数池的范围,其他的字符串等不可变类型也都包含在内一便采用相同的方式处理了,我们只需要记住这是一种优化 机制,至于范围到底多大,无需细究。

发布了46 篇原创文章 · 获赞 4 · 访问量 1290

猜你喜欢

转载自blog.csdn.net/Yauger/article/details/103812608
今日推荐