学习地址:
撩课 - Python 大数据 + 人工智能 1
撩课 - Python 大数据 + 人工智能 2
撩课 - Python 大数据 + 人工智能 3
撩课 - Python 大数据 + 人工智能 4
撩课 - Python 大数据 + 人工智能 5
撩课 - Python 大数据 + 人工智能 6
撩课 - Python-GUI 编程 - PyQt5
1. 概念
生命周期
指的是一个对象, 从诞生到消亡的过程
当一个对象被创建时, 会在内存中分配相应的内存空间进行存储
当这个对象不再使用, 为了节约内存, 就会把这个对象释放
2. 涉及问题
如何监听一个对象的生命过程?
Python 是如何掌控一个对象的生命?
3. 监听对象生命周期
__new__方法
当我们创建一个对象是, 用于给这个对象分配内存的方法
通过拦截这个方法, 可以修改对象的创建过程
比如: 单例设计模式
__init__方法
每个对象实例化的时候, 都会自动执行这个方法
可以在这个方法里面, 初始化一些实例属性
__del__方法
当对象被释放的时候调用这个方法
可用于在这个方法中清理资源
4. 内存管理机制
存储方面
1. 在 Python 中万物皆对象
不存在基本数据类型
0, 1.2, True, False, "abc"
这些全都是对象
2. 所有对象, 都会在内存中开辟一块空间进行存储
会根据不同的类型以及内容, 开辟不同的空间大小进行存储
返回该空间的地址给外界接收 (称为 "引用"), 用于后续对这个对象的操作
可通过 id() 函数获取内存地址 (10 进制)
通过 hex() 函数可以查看对应的 16 进制地址
3. 对于整数和短小的字符, Python 会进行缓存; 不会创建多个相同对象
此时, 被多次赋值, 只会有多份引用
4. 容器对象, 存储的其他对象, 仅仅是其他对象的引用, 并不是其他对象本身
比如字典, 列表, 元组这些 "容器对象"
全局变量是由一个大字典进行引用
global() 查看
垃圾回收方面
1). 引用计数器
概念
一个对象, 会记录着自身被引用的个数
每增加一个引用, 这个对象的引用计数会自动 + 1
每减少一个引用, 这个对象的引用计数会自动 - 1
举例
引用计数 + 1 场景
对象被创建
p1 = Person()
对象被引用
p2 = p1
对象被作为参数, 传入到一个函数中
log(p1)
这里注意会 + 2, 因为内部有两个属性引用着这个参数
对象作为一个元素, 存储在容器中
l = [p1]
引用计数 - 1 场景
对象的别名被显式销毁
del p1
对象的别名被赋予新的对象
p1 = 123
一个对象离开它的作用域
一个函数执行完毕时
内部的局部变量关联的对象, 它的引用计数就会 - 1
对象所在的容器被销毁, 或从容器中删除对象
查看引用计数
- import sys
- sys.getrefcount(对象)
注意会大一
垃圾回收
主要作用
从经历过 "引用计数器机制" 仍未被释放的对象中, 找到 "循环引用", 干掉相关对象底层机制 (了解 & 难)
怎样找到 "循环引用"?
1. 收集所有的 "容器对象", 通过一个双向链表进行引用
容器对象
可以引用其他对象的对象
列表
元组
字典
自定义类对象
...
非容器对象
不能引用其他对象的对象
数值
字符串
布尔
...
注意: 针对于这些对象的内存, 有其他的管理机制
2. 针对于每一个 "容器对象", 通过一个变量 gc_refs 来记录当前对应的引用计数
3. 对于每个 "容器对象", 找到它引用的 "容器对象", 并将这个 "容器对象" 的引用计数 -1
4. 经过步骤 3 之后, 如果一个 "容器对象" 的引用计数为 0, 就代表这玩意可以被回收了, 肯定是 "循环引用" 导致它活到现在的
如何提升查找 "循环引用" 的性能?
如果程序当中创建了很多个对象, 而针对于每一个对象都要参与 "检测" 过程; 则会非常的耗费性能
所以, 基于这个问题, 产生了一种假设:
越命大的对象, 越长寿
假设一个对象 10 次检测都没给它干掉, 那认定这个对象一定很长寿, 就减少这货的 "检测频率"
基于这种假设, 设计了一套机制
分代回收
机制
1. 默认一个对象被创建出来后, 属于 0 代
2. 如果经历过这一代 "垃圾回收" 后, 依然存活, 则划分到下一代
3. "垃圾回收" 的周期顺序为
0 代 "垃圾回收" 一定次数, 会触发 0 代和 1 代回收
1 代 "垃圾回收" 一定次数, 会触发 0 代, 1 代和 2 代回收
查看和设置相关参数
- import gc
- print(gc.get_threshold())
- gc.set_threshold(700, 10, 5)
垃圾回收器当中, 新增的对象个数 - 消亡的对象个数 , 达到一定的阈值时, 才会触发, 垃圾检测
垃圾回收时机 (掌握 & 简单)
1. 自动回收
触发条件
开启垃圾回收机制
gc.enable()
开启垃圾回收机制 (默认开启)
gc.disable()
关闭垃圾回收机制
gc.isenabled()
判定是否开启
并且
达到了垃圾回收的阈值
垃圾回收器中, 新增的对象个数和释放的对象个数之差到达某个阈值
涉及方法
gc.get_threshold()
获取自动回收阈值
gc.set_threshold()
设置自动回收阈值
2. 手动回收
触发条件
gc.collect()
执行一次垃圾回收 (开关状态无效)
特殊场景
场景条件
Python2.x 版本下, 循环引用, 并且有一个对象都实现了__del__方法
概念
两个对象互相引用对象, 谁的引用计数都是 1
两个对象都
导致结果
无法释放, 进行内存回收
底层原因
无法判别先释放哪个对象, 调用哪一个 del 方法
解决思路
手动解除循环引用
方式 1
预防
尽可能避免循环引用产生
实现
一方使用弱引用代替
方式 2
治疗
在循环引用的产生的前提下
实现
当删除一个引用, 确定以后不再使用时, 手动清空对其他容器对象的引用
测量对象的引用个数
辅助工具
- objgraph
- http://mg.pov.lt/objgraph/
- xdot
- graphviz
来源: http://www.jianshu.com/p/1e6febc5bf41