python中的内存管理

python引用机制

以引用计数为主,分代回收、标记清除为辅的垃圾回收方式,以及对小整型进行缓存和简单字符串驻留的内存池机制

Python动态类型

• 对象是储存在内存中的实体。

• 我们在程序中写的对象名,只是指向这一对象的引用(reference)

• 引用和对象分离,是动态类型的核心

• 引用可以随时指向一个新的对象(内存地址会不一样)

引用计数

python中的每个对象都维护一个引用计数 ob_ref字段

当有新的引用指向该对象的时候,引用计数+1

当有无效的引用发生的时候, 引用计数-1

最后引用计数为0, 销毁对象

img

获取引用计数: 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 "<stdin>", 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

标记清除——主要解决循环引用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值