1 Python垃圾回收

首先引入变量和对象的概念:
  (1)对象:具有已知类型、每个对象都包含有头部信息,标识类型和引用计数器。
  (2)变量:其实是一种指针,指向对象;其指向对象的内存空间,内存空间内的值。

接下来我们先做一个例子,创建一个对象和关联一个变量:

varible1 = 33 # 其中33就是一个对象,variable1就是引用这个对象的变量
varible1
Out[2]: 33

id(varible1)  # 通过id()方法查看其存储地址
Out[3]: 140726279841168

前面对象概念中我们提到了引用计数器,在前面的代码中已经有一个变量 varibel1,那现在我们来看对象 varible1 的引用计数器。使用命令sys.getrefcount(args),这儿显示结果是85,也就是说33这个变量被引用了85次,原因可能是后台程序中对这个变量也有过引用。

varible1 = 33

import sys
sys.getrefcount(varible1)
Out[71]: 85

那我们换一个对象去引用,现在我们再来创建一个 var1 变量, 再赋予它一个对象值。我们分别引用对象和变量名作为参数。

import sys
var1 = [1,2,3]
sys.getrefcount(var1)
Out[75]: 2

发现输出结果比预计的多了1,原因查了一下说是因为getrefcount 的参数为变量名时,它本身会再创建一个对变量名指向的对象进行临时引用

help(sys.getrefcount)
Help on built-in function getrefcount in module sys:

getrefcount(...)
    getrefcount(object) -> integer
    
    Return the reference count of object.  The count returned is generally
    one higher than you might expect, because it includes the (temporary)
    reference as an argument to getrefcount().

现在给把 var1 赋值 给另一个变量 var2,我们发现var1 和 var2 指向的地址都是相同的, 而且引用对象计数是加1,这也证明了变量实际上是一个指向对象的指针,对象可以有多个指针指向。

var2 = var1
sys.getrefcount(var1)
Out[77]: 3

id(var1)
Out[78]: 2995236553928

id(var2)
Out[79]: 2995236553928

现在我们删除一个指向[1,2,3]这个对象的变量,看看计数器会发生怎么样的变化。。。我们可以看到计数器减1

del var2

sys.getrefcount(var1)
Out[82]: 2

那当把var1也删除后呢?这时候 [1,2,3] 这个对象就没有变量去引用,Python回收机制就会自动把这个对象删除,释放内存空间。

2 内存池机制

现在来讲一下Python的内存池机制是怎样的

python 变量内存地址关系图 python变量内存分配_python


Python内存管理机制是一个呈金字塔一样的管理方式。-1,-2层主要由操作系统去管理;0层是基于C的malloc和free去进行分配和释放操作;+1,+2层是由Python管理的内存池,主要用于对象小于256k的内存分配;最上层是对 对象的操作层了。

在这引入用户态和核心态的概念(这儿引用别人的内容):
当一个进程在执行用户自己的代码时处于用户运行态(用户态),此时特权级最低,为3级,是普通的用户进程运行的特权级,大部分用户直接面对的程序都是运行在用户态。Ring3状态不能访问Ring0的地址空间,包括代码和数据;当一个进程因为系统调用陷入内核代码中执行时处于内核运行态(内核态),此时特权级最高,为0级。执行的内核代码会使用当前进程的内核栈,每个进程都有自己的内核栈。。。 用户运行一个程序,该程序创建的进程开始时运行自己的代码,处于用户态。如果要执行文件操作、网络数据发送等操作必须通过write、send等系统调用,这些系统调用会调用内核的代码。进程会切换到Ring0,然后进入3G-4G中的内核地址空间去执行内核代码来完成相应的操作。内核态的进程执行完后又会切换到Ring3,回到用户态。这样,用户态的程序就不能随意操作内核地址空间,具有一定的安全保护作用。这说的保护模式是指通过内存页表操作等机制,保证进程间的地址空间不会互相冲突,一个进程的操作不会修改另一个进程地址空间中的数据。

现在回到我们的话题,当我们创建及引用一个python对象时,需要给对象分配内存空间,假如直接在0层中直接调用malloc和free去分配和释放空间,由于Python是面向对象的一门语言,也就是说一切皆对象,对Python来说对象的创建和释放是很频繁的事,那么频繁的通过0层分配和释放空间(进程频繁的在Ring3和Ring0之间切换)会严重影响Python的执行效率,还会产生内存碎片。
所以Python就创建了一个自己的内存管理机制,去管理小内存的分配和释放(小于256k)。当要创建的内存大于256k时,则有0层里的malloc去分配和释放。