垃圾回收的必要性
为什么现在电动车越来越多?
传统能源车排放的垃圾(二氧化碳、二氧化硫)无法回收
为什么大街上的垃圾箱都要分类?
便于垃圾回收,重复利用
为什么样电脑开时间长了,越来越卡?
程序占用的内存无法释放
程序的垃圾回收
电脑运行一段时间会变慢,大家是亲身体会过,相信大家对于这种情况的处理都有各自的方法,比如:
- 关闭不用的程序
- 结束掉进程
- 关闭一些服务
- 重启电脑
我们会发现,重启的效果是最明显的,原因就在于,程序永远不会完美,通过前三种方法无法释放内存资源,而垃圾回收就是为了尽可能的使程序完美(程序不可能完美,需求始终在变)
本章内容
- 引用计数机制
- Python中的循环数据结构及引用计数
- Python中的GC模块
- Python 内存优化
- Python pep8规范
- Python 命令行参数
引用计数机制
概述
Garbage collection(GC)
现在的高级语言如java,c#等,都采用了垃圾收集机制,而不再是c,c++里用户自己管理维护内存的方式。自己管理内存极其自由,可以任意申请内存,但如同一把双刃剑,为大量内存泄露,悬空指针等bug埋下隐患。
对于一个字符串、列表、类甚至数值都是对象,且定位简单易用的语言,自然不会让用户去处理如何分配回收内存的问题。
python里也同java一样采用了垃圾收集机制,不过不一样的是: python采用的是引用计数机制为主,标记-清除和分代收集两种机制为辅的策略
引用计数原型
python里每一个东西都是对象,它们的核心就是一个结构体:PyObject
PyObject是每个对象必有的内容,其中ob_refcnt就是做为引用计数。当一个对象有新的引用时,它的obrefcnt就会增加,当引用它的对象被删除,它的ob_refcnt就会减少 【python中任何都是对象】
当引用计数为0时,该对象生命就结束了
引用计数优点
- 简单
- 实时性:一旦没有引用,内存就直接释放了。不用像其他机制需要等到特定时机。实时性还带来一个好处:处理回收内存的时间分摊到了平时
引用计数缺点
- 维护引用计数会消耗资源
- 循环引用
list1与list2相互引用,如果不存在其他对象对它们的引用,list1与list2的引用计数也仍然为1,所占用的内存永远无法被回收,这将是致命的。
对于如今的强大硬件,缺点1尚可接受,但是循环引用导致内存泄露,注定python还将引入新的回收机制。( 标记清除和分代收集)
import sys
a=[]#创建a的时候本身就是一种引用,计数会增加
print(gc.get_threshold(a))#函数在查看的时候也会增加引用次数,2次
b=a
print(gc.get_threshold(a))#3次
c=b#间接引用也算一次引用
d=b
e=c
f=e
g=d
print(sys.getrefcount(a)) #八次
GC
主要任务
- 为新生成的对象分配内存
- 识别那些垃圾对象
- 从垃圾对象那回收内存
如果将应用程序比作人的身体:所有你所写的那些优雅的代码,业务逻辑,算法,应该就是大脑。以此类推,垃圾回收机制应该是那个身体器官呢? 垃圾回收就象应用程序的心。像心脏为身体其他器官提供血液和营养物那样,垃圾回收器为你的应该程序提供内存和对象。如果心脏停跳,过不了几秒钟人就完了。如果垃圾回收器停止工作或运行迟缓,像动脉阻塞,你的应用程序效率也会下降,直至最终死掉。
Python中的循环数据结构及引用计数
标记-清除机制
1. 标记-清除机制,顾名思义,首先标记对象(垃圾检测),然后清除垃圾(垃圾回收)
首先初始所有对象标记为白色,并确定根节点对象(这些对象是不会被删除),标记它们为黑色(表示对象有效),将有效对象引用的对象标记为灰色(表示对象可达,但它们所引用的对象还没检查),检查完灰色对象引用的对象后,将灰色标记为黑色。重复直到不存在灰色节点为止。最后白色结点都是需要清除的对象。【黑色的是在活动的对象】
*循环引用在写代码时尽量要避免
import sys
import psutil
import os
import gc
print(gc.get_threshold())
def showMemSize(tag):
pid=os.getpid()
p=psutil.Process(pid)
info=p.memory_full_info()
memory=info.uss/1024/1024
print('{} memory used:{} MB'.format(tag,memory))
pass
# 验证循环引用的情况##两个对象之间循环引用是否有垃圾回收
def func():
showMemSize('初始化')
a=[i for i in range(10000000)]
b=[i for i in range(10000000)]
a.append(b)
b.append(a)
showMemSize('创建列表对象 a b 之后')
# print(sys.getrefcount(a))#打印引用计数
# print(sys.getrefcou