Python内存管理知识整理
2021-01-17 13:16
标签:== 参考资料 判断 pre 使用 href 定义 程序 count() 当创建对象时, Python 立即向操作系统请求内存 可以用id(变量名)来获取该变量所引用对象的内存地址 is关键字用于判断引用是否相同,==用于判断引用的内容是否相同 在Python中,整数和短小的字符,Python都会缓存这些对象,以便重复使用。当我们创建多个等于“123”的引用时,实际上是让所有这些引用指向同一个对象。 当某个对象被创建并赋值给变量时,该对象的引用计数都被设置为1,再次被引用会增加该对象的引用计数,而当对象的引用被销毁,引用计数会减小。 查看一个对象的引用计数: 使用某个对象的引用作为getrefcount的参数时,此参数实际上创建了一个对象的临时引用,因此getrefcount返回的引用计数是该对象实际的引用计数+1 getrefcount不仅仅统计当前代码块对对象的引用计数,还统计了import模块中对对象的引用计数。在python的内置模块中,可能有很多对数字1的引用,因此 一个对象的引用计数变为0后,用户不可能通过任何方式动用这个对象,Python将立即将其释放,并将其占用的内存还给操作系统 引用计数法最主要的缺点在于不能解决对象的循环引用问题 注意:只有容器对象才会产生循环引用的情况,比如列表、字典、用户自定义类的对象、元组等。而像数字,字符串这类简单类型不会出现循环引用。 我们已经不能通过任何变量访问到A、B对象,但是由于它们各包含一个对方对象的引用,因此它们的引用计数无法归零,因此不会被回收。如果仅仅使用引用计数法来管理内存,则会因为循环引用造成内存泄露 为了解决对象的循环引用问题,Python引入了标记-清除和分代回收两种GC机制。 https://andrewpqc.github.io/2018/10/08/python-memory-management/ 跟其名称一样,该算法在进行垃圾回收时分成了两步,分别是: 在标记清除算法中,为了追踪容器对象,需要每个容器对象维护两个额外的指针,用来将容器对象组成一个双端链表,指针分别指向前后两个容器对象,方便插入和删除操作。python解释器(Cpython)维护了两个这样的双端链表,一个链表存放着需要被扫描的容器对象,另一个链表存放着临时不可达对象。 GC第一次遍历所有对象,复制每个对象的引用计数,可以记为gc_ref。假设,每个对象i,该计数为gc_ref_i。Python会遍历所有的对象i。对于每个对象i引用的对象j,将相应的gc_ref_j减1。这一步操作就相当于解除了循环引用对引用计数的影响。 接着,GC第二次遍历所有的容器对象,如果对象的gc_ref值为0,那么这个对象就被标记为unreachable;如果对象的gc_ref不为0,则被标记为reachable,并且会递归地将从该节点出发可以到达的所有节点标记为reachable 被标记为unreachable的对象会被移到Unreachable链表中 回收所有被标记为unreachable的对象 在标记-清除算法执行的过程中,需要扫描整个内存空间,应用程序会被暂停,为了提升工作效率,Python采用了分代回收的策略 弱代假说:年轻的对象通常消亡得快,而老对象则很可能存活更长时间。 python将所有对象分为0、1、2三代,他们对应的是3个链表。 所有新建对象都是0代,当某一代对象经历过垃圾回收,依然存活,则被归入下一代。 如果0代经历一定次数的垃圾回收,则会启动对0代和1代的垃圾回收;当1代也经历了一定次数的垃圾回收,则会启动对0、1、2代的垃圾回收 查看gc相关阙值: 700是被分配的对象与被释放的对象之差(分配内存的数目减去释放内存的数目);后面两个10,表示10次0代垃圾回收后,才会执行一次0、1代的垃圾回收;10次1代垃圾回收后,才会执行一次0、1、2代的垃圾回收 查看gc实时计数情况: 562表示距离上一次0代垃圾检查,Python分配内存的数目减去释放内存的数目 10表示距离上次1代垃圾检查,0代垃圾检查的数量 0表示距离上次2代垃圾检查,1代垃圾检查的数量 gc --- 垃圾回收器接口 Python中的垃圾回收机制 聊聊python的内存管理 python内存分配 Python深入06 Python的内存管理 画说 Ruby 与 Python 垃圾回收 Python内存管理知识整理 标签:== 参考资料 判断 pre 使用 href 定义 程序 count() 原文地址:https://www.cnblogs.com/luozx207/p/12918389.html一切变量皆是对象的引用
>>> a=1
>>> print(id(a))
56780120
>>> a={‘1‘:1}
>>> b={‘1‘:1}
>>> a==b
True
>>> id(a)
44204920L
>>> id(b)
45830760L
>>> a is b
False
>>> a="123"
>>> b="123"
>>> a is b
True
>>> id(a)
45845320L
>>> id(b)
45845320L
引用计数
if __name__ == ‘__main__‘:
from sys import getrefcount
arr = [4,5,6,7,0,1,2]
print(getrefcount(arr))
# 2
>>> from sys import getrefcount
>>> getrefcount(1)
102
循环引用
a = { } # 变量a指向对象A,A的引用计数为 1
b = { } # 变量b指向对象B,B的引用计数为 1
a[‘b‘] = b # B的引用计数增1
b[‘a‘] = a # A的引用计数增1
del a # A的引用计数减 1,最后A对象的引用为 1
del b # B的引用计数减 1, 最后B对象的引用为 1
标记-清除
标记阶段
清除阶段
分代回收
>>> import gc
>>> print(gc.get_threshold())
(700, 10, 10)
>>> print(gc.get_count())
(562, 10, 0)
>>> a={1}
>>> print(gc.get_count())
(563, 10, 0)
>>> del a
>>> print(gc.get_count())
(562, 10, 0)
>>> gc.collect()
0
>>> print(gc.get_count())
(22, 0, 0)
gc.collect(generation=2)
若被调用时不包含参数,则启动完全的垃圾回收。可以通过generation参数指定启动哪一代的垃圾
参考资料