python内存管理与垃圾回收

内存管理与垃圾回收

内存管理

python官方文档-内存管理

我们在python还是需要尽量避免手动管理内存,应该交给python自动管理。

垃圾回收

Python采用的是引用计数机制为主,标记-清理和分代收集两种机制为辅的策略。

引用计数

引用计数就是对对象的引用次数进行计数,可以将其抽象为:

引用计数结构体{
    引用计数;
    引用的对象;
};

那么当引用计数为0的时候,就会进行垃圾回收。

引用计数+1

1、对象被创建时,例如 mark="帅哥"
2、对象被copy引用时,例如 mark2=mark,此时mark引用计数+1
3、对象被作为参数,传入到一个函数中时
4、对象作为一个子元素,存储到容器中时,例如 list=[mark,mark2]

引用计数-1

1、对象别名被显式销毁,例如 del mark
2、对象引用被赋予新的对象,例如mark2=mark3,此时mark引用计数-1(对照引用计数+1的情况下的第二点来看)
3、一个函数离开他的作用域,例如函数执行完成,它的引用参数的引用计数-1
4、对象所在容器被销毁,或者从容器中删除。

查看引用计数

import sys
a = "mark 帅哥"
print(sys.getrefcount(a))

陷阱:需要注意,因为给getrefcount传递了参数a,因此也导致了引用计数+1,所以打印的结果是2,但是实际引用计数是1,故:引用计数=sys.getrefcount(xxx) - 1

引用计数机制优点

1、简单、直观
2、实时性,只要没有了引用就释放资源。

引用计数机制缺点

1、维护引用计数需要消耗一定的资源
2、循环引用时,无法回收。也正是因为这个原因,才需要通过标记-清理和分代收集机制来辅助引用计数机制。

标记-清除

我们来举个例子说明标记-清除的原理。对于数值,字符串是不存在循环引用的。循环引用只会出现在容器对象中。例如:

list1 = [0]
list2 = [1]
list1.append(list2)
list2.append(list1)

由于python中对象都是引用赋值,因此list1中包含它本身,list2中也包含它自身。这样就形成了循环引用。引用计数就无法为0,不被回收。为了解决这个问题,采用了标记-清除。它分为两个阶段:第一阶段是标记阶段,GC会把所有的『活动对象』打上标记,第二阶段是把那些没有标记的对象『非活动对象』进行回收。

关于标记-清除的更多信息参考:https://zhuanlan.zhihu.com/p/51095294

分代回收

对标记清除中的链表进行优化,将那些可能存在循引用的对象拆分到3个链表,链表称为:0/1/2三代,每代都可以存储对象和阈值,当达到阈值时,就会对相应的链表中的每个对象做一次扫描,除循环引用各自减1并且销毁引用计数器为0的对象。

缓存机制

实际上,并不是当引用计数为0的时候,就立即回收内存。因为这样将会导致python频繁的malloc和free,导致程序执行效率下降。引用计数器为0时,不会真正销毁对象,而是将他放到一个名为 free_list 的链表中,之后会再创建对象时不会在重新开辟内存,而是在free_list中将之前的对象来并重置内部的值来使用。

参考链接:
https://zhuanlan.zhihu.com/p/401376495
https://pythonav.com/wiki/detail/6/88/

你可能感兴趣的:(python,python,开发语言,后端)