python的垃圾回收机制的工作原理

什么是垃圾回收,为什么需要垃圾回收?

内存管理

大部分编程语言(包括python)都是使用对象来进行操作,也就是说,在python中万物皆对象(包括你写的数字、字符串、函数、类等等)。这些对象都是存在电脑内存中(因为内存的读写速度相当快)。在python中,所有的变量名都可以看成是对象的引用(指向内存中对象地址的指针)。每当程序中用到一个变量时,程序会依据地址来读取内存中的变量指向的对象。同学们可以用下面的代码查看对象的十进制和十六进制地址。

temp="qwe"
print(id(temp))             #2623140777840
print(hex(id(temp)))        #0x262bf5fcf70

在早期的编程语言中(比如c和c++),程序猿通常需要自己进行内存管理。我们通常在一些博客中看见C++程序猿需要开辟一块内存等等。因为当他们需要创造一个列表或者对象时,需要分配一块内存给他们的变量,当用完时,还需要自己去释放这块内存。这样就会导致两个问题

  • 忘记释放内存:如果在用完变量后不释放内存的话,将会导致内存泄漏(是的,这就是内存泄漏,听名字不像,但是它的确就意味着着分配的内存没有及时释放)。这会导致你的程序使用过多内存,产生严重的问题。
  • 悬空指针:悬空指针代表着一个指向空地址的变量,这是因为内存被释放的太早了。

为了避免这两个问题,python采用了垃圾回收机制。当然不是说,采用垃圾回收机制后,肯定不会发生这些问题,但是能避免绝大部分就足够了。

python中的垃圾回收机制

首先需要假定你的编译器是cpython。

然后,CPython中的垃圾回收有两个方面:

  • 引用计数
  • 分代回收

引用计数回收机制

CPython中的主要的垃圾回收方式是引用计数机制,每当你创建一个python对象时,底层的C对象会包含一个python对象以及一个引用计数器(reference count)。

每当有一个变量指向一个python对象时,它的引用计数器就会增加1,反之一个变量取消对它的引用时,它的计数器就会减少1.当计数器值变为0时,这块内存就会被释放掉。

引用计数的垃圾回收机制有缺点也有优点。缺点是无法解决循环引用的垃圾回收,优点是可以当一个对象的计数器为0时可以很迅速的释放那块内存。

看一下如何查看python对象的引用计数器值,注意,当一个对象被一个函数当做参数时,它的计数器也会加1

import sys
a = 'my-string'
sys.getrefcount(a)
#2

代回收机制

为什么需要有这样一个机制辅助引用计数机制呢,就是为了解决引用计数机制的无法回收循环对象的问题。,下面给大家看一个循环对象,我自己引用自己!强不强!

class MyClass(object):
     pass

a = MyClass()
a.obj = a
del a

这样的话,这个对象的引用计数器的值永远不为0,这时候就需要代回收机制。要理解代回收机制需要理解两个核心概念。

第一个是“代”(generation)的概念,这个代不是代替的意思而是一代的意思。python中一共有三代,不同的对象被划分为不同的代。代回收机制持续跟踪内存中的所有对象,当一个对象呗新建出来的时候会被划分为第一代。当代回收机制开始在第一代执行回收时,假如这个对象没有被释放,它将会被划分为第二代。同样,它要是在第二代没被清除时,会被划分为第三代。代数越高,被检查的概率就越低,因此被释放的概率也越低。

第二个概念是阈值(threshold),对于每一代,python都会设定一个阈值,当这一代的对象数目超过阈值,python就会触发这个垃圾回收进程。每次一个对象没有被回收的时候就会进入更高的一代。通常情况下,这个阈值是700,10,10,同学们可以用以下代码查询

import gc
print(gc.get_threshold())

还可以查询每一代已经有多少数量了

gc.get_count()

同样,这些阈值可以根据需要进行设置,大家可以自己查一查

发布了36 篇原创文章 · 获赞 3 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/tyro_blog/article/details/103314171
今日推荐