pyhon 内存管理

引用计数为主,标记清除和分代回收为辅。
标记清除是为了解决引用计数难以解决的循环引用问题。
分代回收使用空间换取时间,提高垃圾回收的效率。

1 内存分配

查看对象占用内存字节大小使用到sys模块的getsizeof()方法。
getsizeof方法只计算对象直接占用的内存,而不计算对象内所引用对象的内存。
空对象分配的内存并不为空!

import sys

a = None
b = ()
c = ""
d = []
e = set()
f = dict()
print(sys.getsizeof(a))
print(sys.getsizeof(b))
print(sys.getsizeof(c))
print(sys.getsizeof(d))
print(sys.getsizeof(e))
print(sys.getsizeof(f))

结果

16
48
51
64
224
240

除了None对象外,其他空对象都是容器,可以理解为创建这个容器本身就需要占用一定的内存,还有一部分内存是对象在初始化的时候预分配。这就是我们看到的空对象也占用这么大内存原因。

1.1 环状双向链表refchain

在python程序中创建的任何对象都会放在refchain链表中。

refchain

通用
-上一个对象
-下一个对象
-类型
-引用计数
C源码中每个对象中都有的值:PyObject结构体(有4个值)

由多个元素组成的对象:PyObject结构体+ob_size

int类型
value
list类型
items(列表元素),元素个数

2 引用计数

结构体中ob_refcnt,即引用计数器,值默认为1.
sys.getrefcount()
调用该函数会自身创建一个临时的引用,因此返回值会比期望多1.

记录所使用的对象各有多少引用。
当一个对象被创建,引用计数为1.
当对象的引用计数为0,则会被当做垃圾回收
-对象从refchain中移除
-将对象销毁,内存归还

2.1 增加引用计数

-创建对象
-同一个对象被赋值给其他变量
-作为参数传递给函数、方法、类实例
-被赋值为一个容器对象的成员

容器对象
可以保留指向其他对象的应用的对象就是容器对象。
代表:元组,列表,字典。
非容器对象有字符串和数值等,这些对象不能保留指向其他对象的引用。

2.2 引用计数减少

-离开其作用范围,比如函数、方法、类实例结束
-对象别名被显式销毁(变量名对应的对象引用计数减1)
del a
-对象从一个容器对象中移除
pop(),remove()
-容器对象本身被销毁
del mylist

import sys

a = "mm"
print(sys.getrefcount("mm"))
b = a
print(sys.getrefcount("mm"))
t = id("mm")
print(sys.getrefcount("mm"))
tmp = [1,2,"mm"]
print(sys.getrefcount("mm"))
tmp.remove(("mm"))
print(sys.getrefcount("mm"))
del b
print(sys.getrefcount("mm"))
tmp = [1,2,"mm"]
print(sys.getrefcount("mm"))
del tmp
print(sys.getrefcount("mm"))
5
6
6
7
6
5
6
5
引用计数器

3 标记清除

3.1 循环引用

并不是所有的Python对象都会发生循环引用。有些对象可能保留了指向其他对象的引用,这些对象可能引起循环引用。
容器对象中都被分配了用于循环引用垃圾回收的头结构体。


循环引用

由于各自引用计数器=1,所以不会被回收。
但是由于变量名被销毁,以后不会再有变量指向这块内存,所以这两个链表会一直存在内存中,永远不会被销毁,造成内存泄漏。

所以只用引用计数器是远远不够的,因此引入标记清除。

3.2 标记清除

在python底层再去维护一个链表,存放可能存在循环引用的对象(列表,字典,元组,集合)

两个链表

在内部某种情况下扫描可能存在循环应用的链表中的每个元素,检查是否有循环引用,如果有则让双方的引用计数器-1,如果是0,则垃圾回收。

4 分代回收

-什么时候扫描?
-可能存在循环引用链表的扫描代价比较大

将链表元素分成3代,也就维护3个链表。
0代:0代中对象个数达到700个扫描1次。
1代:0代扫描10次,则1代扫描1次。
2代:1代扫描10次,则2代扫描1次。
最开始都加入到0代,0代中对象个数达到700,对0代进行扫描。
存在循环引用则引用计数器-1,引用计数器=0的垃圾回收,剩下的对象升级作为1代。此时1代标记0代扫描1次。
重复上述过程。
是为了提升标记清除的效率。


总结

5 缓存机制

优化机制。

5.1 池 (int)

为了避免重复的创建和销毁常见对象,维护池。
启动解释器的时候,python内部会帮我们创建-5~257的对象。
内部不会开辟内存,直接从池中获取。

5.2 free_list

当一个对象的引用计数器为0是,按理说应该回收。
但是在python内部不会直接回收,而是将对象添加到free_list链表中当缓存。
以后再去创建对象时,不再重新开辟内存,而是直接使用free_list。
把相同数据类型的对象内部数据重新初始化就行,再放到refchain.
free_list有个数的限制。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容