简单理解python的垃圾回收机制

  关键词:垃圾回收、引用计数、分代回收、标记-清除

  前言理解python中变量的定义:抽象理解python中变量的定义过程


1、垃圾回收机制的基本组成:

  python采用的是以引用计数为主,以分代回收和标记清除为辅的垃圾回收机制

2、详细分析垃圾回收机制:

(1)首先是引用计数:

  在python中,每创建一个对象,那么python解释器会自动为其设置一个特殊的变量,这个变量称为引用计数(初始值默认是1)。一旦有一个新变量指向这个对象,那么这个引用计数的值就会加1。如果引用计数的值为0。那么python解释器的内存管理系统就会自动回收这个对象所占的内存空间,删除掉这个对象。

①引用计数+1的情况:

  • 对象被创建,例如a = "laoliang"
  • 对象被引用,例如b = a
  • 对象被作为参数,传入到一个函数中,例如fun(a)
  • 对象作为一个元素,存储在容器中,例如data_list=[a,b]

②引用计数-1的情况:

  • 对象的别名被显式销毁,例如del a
  • 对象的别名被赋予新的对象,例如a = 24
  • 一个对象离开它的作用域,例如func函数执行完毕时,func函数中的局部变量(全局变量不会)
  • 对象所在的容器被销毁,或从容器中删除对象

③查看一个对象的引用计数:

import sys
a = "hello laoliang"
sys.getrefcount(a)

注意:查看a对象的引用计数时,比正常计数大1,因为调用函数的时候传入a,这会让a的引用计数+1。

(2)既然已经有引用计数了,那么为什么还要提出分代回收呢?

原因就是引用计数没办法解决“循环引用”的情况。

①“循环引用”分析:

a = ["laoliang", ]   # 语句1
b = ["laowang", ]    # 语句2
a.append(b)          # 语句3
b.append(a)          # 语句4# 此时对象的值:a = ["laoliang", b]   b = ["laowang", a]
del a                # 语句5
del b                # 语句6# 执行完语句5和语句6是希望同时删除掉a对象和b对象

分析:

  执行完语句1到语句4之后,会形成上图左边所示的对象引用关系。

  由上图可知,在执行“del a”语句之后,只是删除了对象的引用,也就是此时a变量这个名字被删除,也就是此时对象[b, "laoliang"]的引用计数减1;执行"del b"语句也是同样的情况。

  此时对象[b, "laoliang"]中有一个引用,指向了[a, "laowang"],所以此时[b, "laoliang"]的引用计数不为0,所以存在于内存中,不真正被删除。[a, "laowang"]也是一个道理。但是,此时,由于显式指向它们的变量已经不存在了,所以也没办法删除了,就会导致它们一直存在于内存空间中。 这就是循环引用出现的问题。 此时,单靠引用计数没办法解决问题。所以便提出了分代回收。

②分代回收:

注意:在分代回收中,如果某对象的引用计数为0,那么它所占的内存空间同样也会被python解释器回收。

a、此时在python中每创建一个对象,那么就会把对象添加到一个特殊的“链表”中,这个链表称为"零代链表"。每当创建一个新的对象,那么就会将其添加到零代链表中。当这个"零代链表"中的对象个数达到某一个指定的阀值的时候,python解释器就会对这个"零代链表"进行一次“扫描操作”。这个“扫描操作”所做的工作是查找链表中是否存在循环引用的对象,如果在扫描过程中,发现有互相引用的对象,那么会让这些对象的引用计数都减少1。此时,如果某些对象引用计数变成0,那么就会被python解释器回收其所占用的内存空间;如果对象的引用计数仍然不为0,那么会把此时存活的对象迁移到“一代链表”中。

b、同样,python解释器也会在一定的情况下,也扫描“一代链表”,判断其中是否存在互相引用的对象。如果存在,那么同样也是让这些对象的引用计数都减少1。此时,如果某些对象引用计数变成0,那么就会被python解释器回收其所占用的内存空间;如果对象的引用计数仍然不为0,那么会把此时存活的对象迁移到“二代链表”中。

c、同样,python解释器也会在一定的情况下,也会扫描"二代链表",判断其中是否存在互相引用的对象。如果存在,那么同样也是让这些对象的引用计数都减少1。此时,如果某些对象引用计数变成0,那么就会被python解释器回收其所占用的内存空间;如果对象的引用计数仍然不为0,那么会把此时存活的对象迁移到一个新的特殊的内存空间。此时重新进行"零代链表 -> 一代链表 -> 二代链表"的循环。

这就是python的分代回收机制。

(3)那么既然已经有分代回收了,那么为什么又要提出标记-清除呢?

原因就是分代回收没办法解决“误删”的情况。

①“误删”分析:

a = ["laoliang", ]   # 语句1
b = ["laowang", ]    # 语句2
a.append(b)          # 语句3
b.append(a)          # 语句4  # 此时对象的值:a = ["laoliang", b]   b = ["laowang", a]。 ["laoliang", b]、["laowang", a]的引用计数都为2
del a                # 语句5# 此时["laoliang", b]的引用计数为1, ["laowang", a]的引用计数为2# 执行完语句5只希望删除a对象

  如果按照分代回收的方式来处理上述语句。那么,python解释器在执行完语句5之后。在一定的情况下进行查找循环引用对象的时候,会发现此时["laowang", a]对象和["laoliang", b]对象存在互相引用的情况。所以此时就会让这两个对象的引用计数减1。此时,["laoliang", b]对象的引用计数为0,所以["laoliang", b]对象被真正删除,但是其实此时["laowang", a]对象中是有一个变量引用原来的["laoliang", b]对象的。如果["laoliang", b]对象被真正删除的话,那么此时["laowang", a]对象中的a变量就没有用了,就没有办法访问了。但是其实我们是希望它有用的,所以这个时候就出现“误删”的情况了。所以此时就需要结合“标记-清除”来解决问题了。

②标记-清除:

  • 此时同样是检测链表中的相互引用的对象,然后让它们的引用计数减1之后;
  • 但是此时会将所有的对象分为两组:死亡组(death_group)和存活组(survival_group),把引用计数为0的对象添加进死亡组,其它的对象添加进存活组;
  • 此时会对存活组的对象进行分析,只要对象存活,那么其内部的对象当然也必须存活。如果发现内部对象死亡,那么就会想方设法让其活过来,通过这样子就能保证不会删错对象了。

eg:对于①的题目的重新分析:

  在检查死亡组的时候,会发现[a,"laowang"]对象中的a所指向的对象存在于死亡组中,所以就会想方设法让其复活,此时就能够保证[a, "laowang"]对象中所有的对象都是存活的。

  实际运行分析:

    在删除了a对象之后,b对象中的内部的对象都是可以访问的。这就是因为使用了标记-清除。

    

3、综上所述:

  python就是采用的是以引用计数为主,以分代回收和标记清除为辅的垃圾回收机制。三者相辅相成,共同来处理、维护python程序所占用的内存空间。

参考资料:

https://www.jianshu.com/p/6c783517a5e9

https://www.cnblogs.com/saolv/p/8411993.html

https://blog.csdn.net/admin_maxin/article/details/81632580

https://blog.csdn.net/weixin_34232363/article/details/85943385

原文地址:https://www.cnblogs.com/axing-articles/p/11413076.html

时间: 2024-10-12 17:30:38

简单理解python的垃圾回收机制的相关文章

python的垃圾回收机制(GC)

Hello, 大家好, 又出来冒头了. 今天想跟大家分享的是关于python的垃圾回收机制,虽然本人这会对该机制没有很深入的了解, 但是本着热爱分享的原则,还是囫囵吞枣地坐下记录分享吧, 万一分享的过程中开窍了呢.哈哈哈. 首先还是做一下概述吧: 我们都知道, 在做python的语言编程中, 相较于java, c++, 我们似乎很少去考虑到去做垃圾回收,内存释放的工作, 其实是python内部已经做了相应的回收机制, 不用我们自己操心去做内存释放.但是还是有必要了解一下.可以更加深入的了解pyt

详解python的垃圾回收机制

python的垃圾回收机制 一.引子 我们定义变量会申请内存空间来存放变量的值,而内存的容量是有限的,当一个变量值没有用了(简称垃圾)就应该将其占用的内存空间给回收掉,而变量名是访问到变量值的唯一方式,所以当一个变量值没有关联任何变量名时,我们就无法再访问到该变量值了,该变量值就是一个垃圾会被python解释的垃圾回收机制自动回收 二.什么是垃圾回收机制 垃圾回收机制(简称GC)是python解释器自带的一种机制,专门用来回收不可用的变量值所占用的内存空间 三.为什么要用垃圾回收机制 程序运行过

小猿圈python之垃圾回收机制

一.前言 Python 是一门高级语言,使用起来类似于自然语言,开发的时候自然十分方便快捷,原因是Python在背后为我们默默做了很多事情,其中一件就是垃圾回收,来解决内存管理,内存泄漏的问题. 内存泄漏:当程序不停运行,有一部分对象没有作用,但所占内存没有被释放,服务器内存随时间越来越少,最终导致系统的崩溃,所以内存泄漏是一个需要重点关注的问题. Java垃圾回收 半自动 只买新的 不用管扔垃圾 Python全自动 二.python的垃圾回收机制包括引用计数.循环引用,那么我们通过什么来解决呢

Python之垃圾回收机制与用户交互

Python之垃圾回收机制与用户交互 垃圾回收机制 垃圾回收机制(简称GC)是Python解释器自带一种机制,专门用来回收不可用的变量值所占用的内存空间 为什么要用垃圾回收机制? 程序运行过程中会申请大量的内存空间,而对于一些无用的内存空间如果不及时清理的话会导致内存使用殆尽(内存溢出),导致程序崩溃,因此管理内存是一件重要且繁杂的事情,而python解释器自带的垃圾回收机制把程序员从繁杂的内存管理中解放出来. 垃圾回收机制原理分析 Python的GC模块主要运用了"引用计数"(ref

python的垃圾回收机制

进程空间 进程运行时需要在内核中占据一段内存空间,用以存储程序和数据. 每个进程空间分布如下所示: 进程空间的结构 text段:代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域.在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等. data段:数据段(data segment)通常用来存放程序中已初始化的全局变量数据段属于静态内存分配. bss段:bss(Block Started by Symbol) 通常用来存放程序中未初始化的

<转> python的垃圾回收机制

Python的GC模块主要运用了"引用计数"(reference counting)来跟踪和回收垃圾.在引用计数的基础上,还可以通过"标记-清除"(mark and sweep)解决容器对象可能产生的循环引用的问题.通过"分代回收"(generation collection)以空间换取时间来进一步提高垃圾回收的效率. 引用计数机制: python里每一个东西都是对象,它们的核心就是一个结构体:PyObject 1 typedef struct_

理解Android Java垃圾回收机制

Jvm(Java虚拟机)内存模型 从Jvm内存模型中入手对于理解GC会有很大的帮助,不过这里只需要了解一个大概,说多了反而混淆视线. Jvm(Java虚拟机)主要管理两种类型内存:堆和非堆.堆是运行时数据区域,所有类实例和数组的内存均从此处分配.非堆是JVM留给自己用的,包含方法区.JVM内部处理或优化所需的内存(如 JIT Compiler,Just-in-time Compiler,即时编译后的代码缓存).每个类结构(如运行时常数池.字段和方法数据)以及方法和构造方法的代码. 简言之,Jav

理解PHP的垃圾回收机制

什么是垃圾回收机制 使用的是"引用计数"方式进行回收.简单地理解的话,就是每个分配的内存区域都有一个计数器,记录有多少个变量指针指向这片内存.当指向该片内存的指针数量为0,那么该片内存区域就可以被回收. 什么又算垃圾 首先我们需要定义一下"垃圾"的概念, 说简单点是指变量的容器zval还存在,但是又没有任何变量名指向此zval.因此判断是否为垃圾的一个重要标准是有没有变量名指向变量容器zval.. 假设我们有一段PHP代码,使用了一个临时变量$tmp存储了一个字符串

python中垃圾回收机制和对象变量的指向问题,深拷贝

变量.对象和引用 一个变量也就是变量名,当代码第一次赋值时就创建了他,之后的赋值将改变已创建的变量名的值. 变量类型,变量中没有类型之说,类型存在于对象中,变量只是在一个特定的时间对对象的引用. 变量使用,变量出现在表达式中会立即被对象所替代,所有变量在使用前就应该对其明确赋值. 例如 a = 3 为例 python将执行3个步骤完成这个请求 1.创建一个对象来代表3 2.创建一个一个变量a,如果他还没创建的话, 3.将变量与新的对象相连接 变量-引用-对象 对象的垃圾回收机制 当变量名被赋予了