Python内存管理

本文将详细地介绍python内存管理的细节,虽然我们写代码的时候不用去关心复杂的内存管理任务,但是了解python内存管理的流程,能够使我们对数据存储和引用更加得心应手。

内存分配

之前也说过,在python中变量名和类型都无需事先申明(我爱python理由之一),这些都是在赋值的时候决定的。Python解释器承担了内存管理的复杂任务,我们只要负责编写代码即可。我们新创建一个对象,并对这个对象进行各种操作的时候,Python解释器是如何进行内存管理的呢?在这里就要介绍下python内部的引用计数器了。

引用计数器

要保持追踪内存中创建的对象,Python使用了引用计数器这种技术。顾名思义,就是记录所有使用中的对象各有多少引用次数。当对象被创建时,它的引用次数为1,而当它的引用次数变成0时,该对象就会被垃圾回收机制处理,从而释放出内存。那么增加和减少引用计数的方式有哪些呢?

增加引用计数的方式:

对象被创建:

此时变量名x指向对象1,对象1的引用计数为1,如图所示:

《Python内存管理》

别名被创建:

此时变量名y也指向对象1,对象1的引用计数为2,如图所示:

《Python内存管理》

被作为参数传递给函数:

成为容器对象(列表,元组,字典)中的元素:

减少引用计数的方式:

变量名指向另一个对象:

本来变量名x、y都指向对象1,引用次数为2,当把对象2赋值给变量名y时,变量名y不再指向对象1了,此时引用次数变为1,如图所示:

《Python内存管理》

函数结束时:

比如function(x)结束后,对象1的引用次数减少1次。

显式删除别名:

从容器对象中移除:

或者容器对象被删除:

这些方式都会使对象的引用计数减少。

垃圾收集器

垃圾收集器负责释放内存,它会寻找引用计数为0的对象,也会检查那些引用计数大于0但也应该被销毁的对象,比如循环引用。

一个典型的循环引用如下:

有两个模块moduleA 和 moduleB,首先在A中导入B,进入B中,发现B中又导入了A又回到A中,但是A又导入B这就形成了循环引用。一个循环引用发生在至少两个对象互相引用时,也就是说所有的引用都消失时,这些引用仍然存在,这说明单靠引用计数还不够。

因此Python的垃圾收集器实际上是由引用计数器和循环垃圾收集器组成。当一个对象的引用计数为0时,解释器暂停,释放掉这个对象;同时垃圾收集器会检查未通过计数销毁的对象,在这种情况下,解释器暂停,试图清理所有未引用的循环。

Python对象

再次重申,python中一切皆对象。python使用对象来存储数据,所以python是一门面向对象的编程语言。python对象包括三个特性:

  • 身份:身份表示对象的内存地址,可以通过内建函数id()查看。
  • 类型:对象的类型决定了保存什么样的数据,可以进行什么样的操作,可以通过内建函数type()查看。
  • 值:对象包含的数据。

这三个特性在对象被创建的时候就自动产生。

指向相同的对象:

以及:

这两种情况都是将变量名x、y指向对象1。除此之外对于不可变对象(比如:整型对象和字符串对象),不同变量名也会指向相同的对象。

在这里我们认为Python应该会创建两个对象,事实上变量名x、y还是指向了同一个对象1,我们可以通过内建函数id()来查看。

两者的内存地址一样。这是因为python认为这些不可变对象会经常被用到。

指向不相同的对象:

以及:

《Python内存管理》

虽然对象的值是一样的,但是对象的内存地址不同,仍是两个不同的对象,可以通过内建函数id()来验证:

点赞