Python python 内存管理

打工人 · 2021年08月29日 · 1636 次阅读

变量与对象

  • 变量 通过变量指针引用对象,变量指针指向具体对象的内存空间,取对象的值
  • 对象 类型已知,每个对象都包含一个头部信息(头部信息:类型标识符和引用计数器) 关系图如下:

注 1:变量名没有类型,类型属于对象(因为变量引用对象,所以类型随对象),变量引用什么类型的对象,变量就是什么类型

var1 = "hello world"
var2 = var1
id(var1)
id(var2)

注 2:id() 是 python 的内置函数,用于返回对象的身份,即对象的内存地址

# 短字符串
a = "hello"
b = "hello"
print(a is b)          # True

# 整数
a = 12
b = 12
print(a is b)      # True

# 长字符串
a = "id()是python的内置函数,用于返回对象的身份,即对象的内存地址"
b = "id()是python的内置函数,用于返回对象的身份,即对象的内存地址"
print(a is b)      # False

注 3:is 是用来判断两个引用所指的对象是否相同,==是用来判断两个变量对应的值是否相同

由运行结果可知:
1、python 缓存了整数和短字符串,因此每个对象在内存中只存在一份,引用所指对象就是相同的,即使使用赋值语句,也只是创造新的引用,而不是对象本身
2、python 没有缓存长字符串、列表以及其他对象,可以由多个相同的对象,使用赋值语句创建新的对象
3、python 中对大于 256 的整数,会重新分配对象空间地址保存对象;对于字符串来说,如果不包含空格的字符串,则不会重新分配对象空间,对于包含空格的字符串则会重新分配

引用计数

python 中 每个对象都有指向该对象的引用总数 -- 引用计数
查看对象的引用次数:sys.getrefcount()

  • 普通引用
import sys

a = [1, 2, 3]
sys.getrefcount(a)      # 2
b = a
sys.getrefcount(a)      # 3
sys.getrefcount(b)      # 3

注 4:当使用某个引用作为参数,传递给 getrefcount() 时,参数实际上创建了一个临时的引用,因此,getrefcount() 所得到的结果,会比期望的多 1

  • 容器对象

python 的一个容器对象(列表、字典、集合等),可以保护多个对象,实际上,容器对象中包含的并不是元素对象本身,而是指向各个元素对象的引用

  • 引用计数增加

    • 对象被创建
    • 被其他对象引用
    • 作为容器对象的一个元素
  • 引用计数减少

    • 对象的别名被显示的销毁(例如 del 语句)
    • 对象的一个别名被赋值给其他对象
    • 对象从一个列表对象中移除,或列表对象本身被销毁

垃圾回收

当 python 中的对象越来越多,占据越来越多的内存,启动垃圾回收(garbage collection),将没用的对象清除

  • 核心 当 python 的某个对象的引用计数降为 0 时,说明没用任何引用指向该对象,该对象就成为要被回收的垃圾。比如某个新建对象,被分配给某个引用,对象的引用计数变为 1 如果引用被删除,对象的引用计数为 0,那么该对象就可以被垃圾回收
a = [123]
del a
  • 解析 del del a 后,已经没有任何引用指向之前建立的 [123]。列表引用计数变为 0,用户不可能通过任何方式接触或者启动这个对象,当垃圾回收启动时,python 扫描到这个引用计数为 0 的对象,那么该对象就可以被垃圾回收

注 5:
1、垃圾回收时,python 不能进行其他的任务,频繁的垃圾回收将大大降低 python 的工作效率
2、python 只会在特定条件下,自动启动垃圾回收(垃圾对象少就没必要回收)
3、当 python 运行时,会记录其中分配对象(object allocation)和取消分配对象(object deallccation)的次数。当两者的差值高于某个阈值时,垃圾回收才会启动

import gc

gc.get_threshold()          # (700, 10, 10)

阈值分析:

  • 700 即是垃圾回收启动的阈值
  • 每 10 次 0 代垃圾回收,会配合 1 次 1 代的垃圾回收;而每 10 次的 1 代垃圾回收,才会有 1 次的 2 代垃圾回收
    通过 gc.collect() 可手动启动垃圾回收

  • 分代回收
    python 将所有的对象分为 0,1,2 三代。所有创建的对象都是 0 代对象。当某一代对象经历过垃圾回收,仍然存活,那么它就会被归入下一代对象。垃圾回收启动时,一定会扫描所有的 0 代对象。如果 0 代经过一定次数垃圾回收,那么久启动对 0 代和 1 代的扫描清理。当 1 代也经历了一定次数的垃圾回收后,那么会启动对 0,1,2,即对所有对象进行扫描

标记清除

如果两个对象的引用计数都为 1,但是仅仅存在他们之间的循环引用,那么这两个对象都是需要被回收的。也就是说,它们的引用计数虽然表现为非 0,但实际上有效的引用计数为 0,所有先将循环引用摘掉,就会得出这两个对象的有效计数

总结

1、python 使用引用计数和垃圾回收来释放 python 对象的内存
2、引用计数的优点是原理简单,但是会增加 python 运行时间;缺点是无法处理循环引用
3、python 标记清除用于处理循环引用
4、gc module 是 python 垃圾回收机制的接口模块,可以通过该模块启停垃圾回收、调整回收触发的阈值

以上内容摘自:https://blog.csdn.net/weixin_39653717/article/details/110371108

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册