pyth的垃圾回收机制
GC(垃圾回收)负责的主要任务
- 为新生成的对象分配内存
- 识别那些垃圾对象
- 从垃圾对象那回收内存
引用计数机制
- Garbage collection(GC)模块的使用(垃圾回收)
- 现在的高级语言如java,c#等,都采用了垃圾收集机制,而不再是c,c++里用户自己管理维护内存的方式。自己管理内存极其自由,可以任意申请内存,但如同一把双刃剑,为大量内存泄露,悬空指针等bug埋下隐患。
- 对于一个字符串、列表、类甚至数值都是对象,且定位简单易用的语言,自然不会让用户去处理如何分配回收内存的问题。
- python里也同java一样采用了垃圾收集机制,不过不一样的是: python采用的是引用计数机制为主,标记-清除和分代收集两种机制为辅的策略
import sys
# 创建时候1次,查看时候一次
a=[]
print('a被引用的次数是:{}'.format(sys.getrefcount(a)))
# 上面查看结束后会自动释放,所以创建1次,赋值1次,查看1次
b=a
print('a被引用的次数是:{}'.format(sys.getrefcount(a)))
print('b被引用的次数是:{}'.format(sys.getrefcount(b)))
# 被间接引用也算引用
c=b
d=c
print('a被引用的次数是:{}'.format(sys.getrefcount(a)))
引用机制的优点:
- 简单
- 实时性:一旦没有引用,内存就直接释放了。不用像其他机制需要等到特定时机。实时性还带来一个好处:处理回收内存的时间分摊到了平时
引用机制的缺点:
- 维护引用计数会消耗资源
- 循环引用就不适用了
list1 = []
list2 = []
list1.append(list2)
list2.append(list1)
list1与list2相互引用,如果不存在其他对象对它们的引用,list1与list2的引用计数也仍然为1,所占用的内存永远无法被回收,这将是致命的。对于如今的强大硬件,缺点1尚可接受,但是循环引用导致内存泄露,注定python还将引入新的回收机制。(标记清除和分代收集)
Python中的循环数据结构及引用计数
标记-清除机制
import psutil
import os
def showMemSize(tag):
pid=os.getpid() # 得到进程id
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 之后')
pass
func()
showMemSize('完成时候的')
import psutil
import os
import sys
def showMemSize(tag):
pid=os.getpid() # 得到进程id
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.getrefcount(b))
del a
del b
pass
func()
showMemSize('完成时候的')
import psutil
import os
import gc # 导入垃圾回收机制
def showMemSize(tag):
pid=os.getpid() # 得到进程id
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 之后')
pass
func()
gc.collect() # 利用gc手动释放内存
showMemSize('完成时候的')
Python中gc模块
有三种情况会触发垃圾回收:(默认是开启垃圾回收的)
- 当gc模块的计数器达到阀值的时候,自动回收垃圾
- 调用gc.collect(),手动回收垃圾
- 程序退出的时候,python解释器来回收垃圾
python内存优化
小整数与大整数对象池
Python为了优化速度,使用了小整数对象池, 避免为整数频繁申请和销毁内存空间。 Python 对小整数的定义是 [-5, 256] 这些整数对象是提前建立好的,不会被垃圾回收。
python命令行参数
命令行参数-sys模块(1)
在使用python开发脚本,作为一个运维工具,或者其他工具需要接受用户参数运行时,这里就可以用到命令行传参的方式,可以给使用者提供一个比较友好的交互体验。
1、新建一个test.py
# Python可以 sys模块中的 sys.argv 来获取命令行参数
import sys
print('参数个数为:', len(sys.argv), '个参数。')
print('参数列表:', str(sys.argv))
2、一般我们不直接执行,而是在路径下,用命令行参数执行
命令行参数-sys模块(2)
argv 返回命令行参数是一个列表,第一个元素就是 py文件的文件名。如果只想获取参数不需要获取文件名,sys.argv也支持python字符串中的切片 修改代码如下:
# Python可以 sys模块中的 sys.argv 来获取命令行参数
import sys
print('参数个数为:', len(sys.argv), '个参数。')
print('参数列表:', str(sys.argv[1:])) # 切片形式从第二个开始提取数据
第一个是文件名,所以可以切片形式提取。