6. Python的内存管理
建议:完成基础语法的学习后再看
内存是每一个程序都要打交道的地方,不同于C类语言的手工管理,Python是一门动态的面向对象的解释型语言,对象与引用是分离的。内存也是由解释器动态管理的。 Python中对于对象的赋值操作就是一个创建对象与引用的过程。在第一章我们提到的赋值语句a=1
就是将1
这个对象与a
引用了起来。
查看内存地址
Python中有一个内置函数id()
,就是用来返回一个对象的内存地址的。而利用hex()
,我们就能看到实际的十六位内存地址。 例如:
引用计数
在Python中,一切皆对象,为了提升效率,在一定情况下,一个对象会被引用多次。 引用计数就是计算一个对象被引用了多少次的一个量化的表示。
引用计数的增加
赋值语句
实参传参
引用计数的减少
函数运行结束
当用
del()
删除某个引用时如果某个引用指向对象A,当这个引用被重新定向到某个其他对象B时,对象A的引用计数减少。
引用计数的查看
我们可以使用
sys
模块中的getrefcount()
,来查看某个对象的引用计数。需要注意的是,当使用某个引用作为参数,传递给
getrefcount()
时,参数实际上创建了一个临时的引用。因此,getrefcount()所得到的结果,会比期望的多1。特殊点
-5到256的这些数字,Python中默认会创建对象来优化,所以会提前缓存好,在使用过程中直接引用就可以了。但是超过这个范围,只有在同一代码块中出现才会有相同的内存地址。短字符串也会在创建之后会被引用多次。
True
True
False
True
False
False
对象间的引用
Python的一个容器对象,比如列表、词典等,可以包含多个对象。实际上,容器对象中包含的并不是元素对象本身,是指向各个元素对象的引用。列表中如果引用列表,可能会有意想不到的问题。参见copy模块。 容器对象的引用可能构成很复杂的拓扑结构,因此在刚开始的阶段不建议进行容器对象的引用。 两个对象可能相互引用,从而构成所谓的引用环(reference cycle)。
即使是一个对象,只需要自己引用自己,也能构成引用环。
引用环会给垃圾回收机制带来很大的麻烦。
垃圾回收
Python中,拥有一种管理内存的机制,就是垃圾回收。从基本原理上,当Python的某个对象的引用计数降为0时,说明没有任何引用指向该对象,该对象就成为要被回收的垃圾了。比如某个新建对象,它被分配给某个引用,对象的引用计数变为1。如果引用被删除,对象的引用计数为0,那么该对象就可以被垃圾回收。这时候我们称之为标记清除。 但是一个对象的引用计数减为零之后不会被立即清理掉,Python只会在特定条件下启动垃圾回收。当Python运行时,会记录其中分配对象(object allocation)和取消分配对象(object deallocation)的次数。当两者的差值高于某个阈值时,垃圾回收才会启动。
我们可以通过gc模块的get_threshold()方法,查看该阈值:
当然,我们也可以手动启动垃圾回收,即使用gc.collect()
来进行垃圾回收。但由于频繁的垃圾回收具有效率问题,因此需要考虑是否需要进行后再操作。
分代回收
Python将所有的对象分为0,1,2三代。所有的新建对象都是0代对象。当某一代对象经历过垃圾回收,依然存活,那么它就被归入下一代对象。垃圾回收启动时,一定会扫描所有的0代对象。如果0代经过一定次数垃圾回收,那么就启动对0代和1代的扫描清理。当1代也经历了一定次数的垃圾回收后,那么会启动对0,1,2,即对所有对象进行扫描。
参考
Last updated
Was this helpful?