- python引用机制
- ==引用计数==
- ==垃圾回收==
- 缓冲池
以引用计数为主,分代回收、标记清除为辅的垃圾回收方式,以及对小整型进行缓存和简单字符串驻留的内存池机制
Python动态类型
• 对象是储存在内存中的实体。
• 我们在程序中写的对象名,只是指向这一对象的引用(reference)
• 引用和对象分离,是动态类型的核心
• 引用可以随时指向一个新的对象(内存地址会不一样)
引用计数python中的每个对象都维护一个引用计数 ob_ref字段
当有新的引用指向该对象的时候,引用计数+1
当有无效的引用发生的时候, 引用计数-1
最后引用计数为0, 销毁对象
获取引用计数: getrefcount()
>>> a = [1, 2, 3]
>>> getrefcount(a)
2
>>> b = a
>>> getrefcount(a)
3
当使用某个引用作为参数,传递给getrefcount()时,参数实际上创建了一个临时的引用。因此,getrefcount()所得到的结果,会比期望的多1。
增加引用计数
当一个对象A被另一个对象B引用时,A的引用计数将增加1
>>> a = [1, 2, 3]
>>> getrefcount(a)
2
>>> b = [a, a]
>>> getrefcount(a)
4
# 因为对象b引用对象a,所以对象c引用对象b时实际上也是引用对象a
# 并且a,b,c指向同一个引用,引用计数相同
>>> a = [1, 2, 3]
>>> b = a
>>> c = b
>>> getrefcount(a)
4
>>> getrefcount(b)
4
>>> getrefcount(c)
4
减少引用计数
del删除或重新引用时,引用计数会变化(del只是删除引用)
>>> a = [1, 2, 3]
>>> b = a
>>> getrefcount(b)
3
>>> del a
>>> getrefcount(b)
2
>>> a = [1, 2, 3]
>>> b = a
>>> getrefcount(a)
3
>>> del b
>>> getrefcount(a)
2
>>> a = [1, 2, 3]
>>> b = a
>>> getrefcount(b)
3
>>> a = 300
>>> getrefcount(b)
2
循环引用
>>> from sys import getrefcount
>>> x = [1]
>>> y = [2]
>>> x.append(y)
>>> y.append(x)
>>> getrefcount(x)
3
>>> getrefcount(y)
3
>>> del x # x的引用计数-1,但是不为0
>>> del y
>>> getrefcount(x)
Traceback (most recent call last):
File "" , line 1, in <module>
NameError: name 'x' is not defined
对于上面相互引用的情况,如果不存在其他对象对他们的引用,这两个对象
所占用的内存也还是无法回收,从而导致内存泄漏
根据引用计数的规律,出现循环引用的情况,内存是无法通过引用计数来释放
这种情况就会造成内存泄露
内存泄露:有一部分没存被占用无法释放,进程又无法访问(站着茅坑不拉屎)
后果:
内存溢出 (oom – out of memory): 内存不够,程序需要的内存大于系统空闲内存
引用计数机制的优点:
• 简单
• 实时性
引用计数机制的缺点:
• 维护引用计数消耗资源
• 循环引用时,无法回收
垃圾回收当Python的某个对象的引用计数降为0时,可以被垃圾回收
gc机制
• GC作为现代编程语言的自动内存管理机制,专注于两件事
• 找到内存中无用的垃圾资源
• 清除这些垃圾并把内存让出来给其他对象使用。
当Python运行时,会记录其中分配对象(object allocation)和取消分配对象(object deallocation)的次数。当两者的差值高于某个阈值时,垃圾回收才会启动。
>>> import gc
>>> gc.get_threshold()
(700, 10, 10)
三种情况触发垃圾回收
• 调用gc.collect()
• GC达到阀值时
• 程序退出时
分代回收 —— 启动垃圾回收的时候确定扫描哪些对象的
标记清除——主要解决循环引用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4uPPegzF-1652093028516)(C:\Users\lier\AppData\Roaming\Typora\typora-user-images\image-20220509184056391.png)]
缓冲池整数对象缓冲池
对于[-5,256] 这样的小整数,系统已经初始化好,可以直接拿来用。而对于其他的大整数,系统则提 前申请了一块内存空间,等需要的时候在这上面创建大整数对象。
预先创建好一个小整型数池 [-5, 256]
>>> a = 3
>>> getrefcount(a)
70
>>> a = 1
>>> getrefcount(a)
934
>>> a = 300
>>> getrefcount(a)
2
>>> a = -5
>>> getrefcount(a)
3
>>> a = -6
>>> getrefcount(a)
2
>>> a = 256
>>> getrefcount(a)
36
>>> a = 257
>>> getrefcount(a)
2
当定义的值不在[-5, 256]这个整型池中时,引用计数就只有1
当在[-5, 256]这个整型池中时,直接从整型池中引用,引用计数为多个
字符串驻留区
>>> str1 = "abdssa"
>>> getrefcount(str1)
2
>>> str = "abdssa"
>>> getrefcount(str)
3
>>> id(str1)
140017852065248
>>> id(str)
140017852065248
单个字符,创建之后会存放在字符串驻留区
多个字符,创建之后如果没有特殊字符,就会存放在字符串驻留区
>>> str1 = "abc xyz"
>>> str2 = "abc xyz"
>>> id(str1)
140017852065472
>>> id(str2)
140017987252328
>>> str1 = "abc123"
>>> str2 = "abc123"
>>> id(str1)
140017987252440
>>> id(str2)
140017987252440
>>> str1 = "#"
>>> str2 = "#"
>>> id(str1)
140017988334064
>>> id(str2)
140017988334064
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)