【Python】内存管理机制

1、垃圾回收
2、缓存机制

1、垃圾回收
垃圾回收机制:
引用计数
标记清除
分带回收

描述垃圾回收之前先引入一个概念:refchain双向环状链表。这个链表的作用很强大,python内部会维护一个这样的链表,当python程序中一旦创建对象,都会把创建的对象添加到refchain中,这个链表保存了所有创建的对象,并且每个对象都包含了上下对象的指针、引用计数和对象的类型。

引用计数:
在refchain双向环状链表中所有的对象内部都有一个自己的引用计数器,表示该对象被引用的次数。当一个python的对象被变量引用时,引用计数+1,不在被变量引用时,引用计数-1,当对象的引用计数为0时,对象就会被当做垃圾回收掉(对象从refchain链表中摘除,释放内存(不考虑缓存等特殊情况))

但引用计数会存在一个问题:循环引用

循环引用:两个对象相互引用。
场景:如果没有变量去引用两个对象,并且两个对象存在循环引用,那么这两个对象的引用计数不为0,导致它们永远不会被使用或者销毁

存在循环引用的对象:
列表
元组
字典
集合

自定义类等能进行数据嵌套的类型
后果:项目中如果存在大量这种对象,就会导致内存一直被消耗,直到内存被耗尽,程序崩溃;当重启系统的时候内存又会空闲很多。
解决办法:引入标记清除和分带回收
标记清除:
创建特殊链表专门用于保存列表、元组、字典、集合、自定义类等对象,之后再去检查这个链表中的对象是否存在循环引用,如果存在则让双发的引用计数-1,当引用计数为0时,则是垃圾,进行回收。否则不是垃圾。

分带回收:(阈值)
对标记清除的链表进行优化,将那些可能存在循环引用的对象拆分到3个链表中,链表分为:0/1/2三代,每代都可以存储对象和阈值,当达到阈值时,就会对相应的链表中的每个对象做一次扫描。扫描的结果是:如果对象存在循环引用,则对象的引用计数都-1,为0的话则视为垃圾回收,如果不是垃圾,则将对象放入下一代链表中。

0/1/2三代的扫描时机和阈值:

0代:当0代链表的对象个数达到700个时会扫描一次
1代:当0代链表扫描10次时扫描一次
2代:当1代链表扫描10次时扫描一次

2、python缓存机制
可以从上文了解到如果对象的引用计数为0时,就会被销毁并释放内存。实际并不是这样子,因为频繁地创建和销毁对象的代价很大并且会影响程序的执行效率。为了解决这个问题,python中引入了 缓存机制 。

例如:引用计数为0时,并不会真正的销毁对象,而是将它放到一个链表中,之后再创建对象时不会再重新开辟新的内存,而是在这个链表中将之前的对象进行重新赋值来使用

不同的数据类型,有不同的缓存机制。

2.1 池(int)
为了避免重复创建和销毁一些常见的对象,池中会初始化[-5~257]范围的整型对象,创建这些对象时,直接从池中获取对象并不会重新开辟新的内存

2.2 free_list(float、list、tuple、dict)
当一个对象的引用计数为0时,按理说应该回收,但内部不会回收,而是将对象添加到free_list链表中当缓存,以后再创建对象对应的类型时,不再开辟新内存,而是直接从free_list中获取

数据类型 free_list缓存对象个数
float 100
tuple 20
dict 80
list 80
对于tuple,存储的20个元组对象特点为:第一个为空元组、第二个为元素只有一个的元组,接下来以此类推…

3、调优手段
手动垃圾回收
避免循环应用
调高垃圾回收阈值

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章