Python专家解读垃圾回收<<一>>

简介: Python专家解读垃圾回收<<一>>

没有坚定不移的信心,任何行动都会失败。——华·欧文


1. 前言



Garbage collection(GC)


现在的高级语言如java,c#等,都采用了垃圾收集机制,而不再是c,c++里用户自己管理维护内存的方式。自己管理内存极其自由,可以任意申请内存,但如同一把双刃剑,为大量内存泄露,悬空指针等bug埋下隐患。对于一个字符串、列表、类甚至数值都是对象,且定位简单易用的语言,自然不会让用户去处理如何分配回收内存的问题。python里也同java一样采用了垃圾收集机制,不过不一样的是: python采用的是引用计数机制为主,标记-清除和分代收集两种机制为辅的策略。


2. 引用计数机制



python里每一个东西都是对象,它们的核心就是一个结构体:PyObject


typedef struct_object {
        int ob_refcnt;
        struct_typeobject *ob_type;
    } PyObject;


PyObject是每个对象必有的内容,其中ob_refcnt就是做为引用计数。当一个对象有新的引用时,它的ob_refcnt就会增加,当引用它的对象被删除,它的ob_refcnt就会减少


#define Py_INCREF(op)   ((op)->ob_refcnt++) //增加计数
   #define Py_DECREF(op) \ //减少计数
        if (--(op)->ob_refcnt != 0) \
            ; \
        else \
             __Py_Dealloc((PyObject *)(op))


当引用计数为0时,该对象生命就结束了。


引用计数机制的优点

  1. 简单
  2. 实时性:一旦没有引用,内存就直接释放了。不用像其他机制等到特定时机。实时性还带来一个好处:处理回收内存的时间分摊到了平时。


引用计数机制的缺点

  1. 维护引用计数消耗资源
  2. 循环引用


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


list1与list2相互引用,如果不存在其他对象对它们的引用,list1与list2的引用计数也仍然为1,所占用的内存永远无法被回收,这将是致命的。对于如今的强大硬件,缺点1尚可接受,但是循环引用导致内存泄露,注定python还将引入新的回收机制。(标记清除和分代收集)


3. 画说 Python 垃圾回收



应用程序那颗跃动的心

GC系统所承担的工作远比"垃圾回收"多得多。实际上,它们负责三个重要任务。它们

  1. 为新生成的对象分配内存
  2. 识别那些垃圾对象,并且
  3. 从垃圾对象那回收内存


如果将应用程序比作人的身体:所有你所写的那些优雅的代码,业务逻辑,算法,应该就是大脑。以此类推,垃圾回收机制应该是那个身体器官呢?(我从RuPy听众那听到了不少有趣的答案:腰子白血球 :) )


我认为垃圾回收就是应用程序那颗跃动的心。像心脏为身体其他器官提供血液和营养物那样,垃圾回收器为你的应该程序提供内存和对象。如果心脏停跳,过不了几秒钟人就完了。如果垃圾回收器停止工作或运行迟缓,像动脉阻塞,你的应用程序效率也会下降,直至最终死掉。

一个简单的例子: 运用实例一贯有助于理论的理解。下面是一个简单类,我们今天就以此为例:

640.png


Python 的对象分配


我们已经了解了Ruby预先创建对象并将它们存放在可用列表中。那Python又怎么样呢?

尽管由于许多原因Python也使用可用列表(用来回收一些特定对象比如 list),但在为新对象和变量分配内存的方面Python和Ruby是不同的。

例如我们用Pyhon来创建一个Node对象:


640.png

当创建对象时Python立即向操作系统请求内存。(Python实际上实现了一套自己的内存分配系统,在操作系统堆之上提供了一个抽象层。但是我今天不展开说了。)

当我们创建第二个对象的时候,再次像OS请求内存:

看起来够简单吧,在我们创建对象的时候,Python会花些时间为我们找到并分配内存。


Ruby 开发者住在凌乱的房间里

640.png

Python 开发者住在卫生之家庭


640.png


Python与Ruby的垃圾回收机制颇为不同。让我们回到前面提到的三个Python Node对象:


640.png

在内部,创建一个对象时,Python总是在对象的C结构体里保存一个整数,称为 引用数。期初,Python将这个值设置为1:

640.png


值为1说明分别有个一个指针指向或是引用这三个对象。假如我们现在创建一个新的Node实例,JKL:

640.png

与之前一样,Python设置JKL的引用数为1。然而,请注意由于我们改变了n1指向了JKL,不再指向ABC,Python就把ABC的引用数置为0了。


此刻,Python垃圾回收器立刻挺身而出!每当对象的引用数减为0,Python立即将其释放,把内存还给操作系统:

640.png


上面Python回收了ABC Node实例使用的内存。记住,Ruby弃旧对象原地于不顾,也不释放它们的内存。Python的这种垃圾回收算法被称为引用计数。是George-Collins在1960年发明的,恰巧与John McCarthy发明的可用列表算法在同一年出现。就像Mike-Bernstein在6月份哥谭市Ruby大会杰出的垃圾回收机制演讲中说的: "1960年是垃圾收集器的黄金年代..."


Python开发者工作在卫生之家,你可以想象,有个患有轻度OCD(一种强迫症)的室友一刻不停地跟在你身后打扫,你一放下脏碟子或杯子,有个家伙已经准备好把它放进洗碗机了!

现在来看第二例子。加入我们让n2引用n1:


640.png

上图中左边的DEF的引用数已经被Python减少了,垃圾回收器会立即回收DEF实例。同时JKL的引用数已经变为了2 ,因为n1和n2都指向它。


4. 小结



由于篇幅的关系,我们今天就写到这里,下篇我们继续Python垃圾回收机制  (二),欢迎点赞转发和关注。


5. 关注公众号



 微信公众号:堆栈future

相关文章
|
6天前
|
人工智能 Java 机器人
【python】python的垃圾回收机制(详细讲解)
【python】python的垃圾回收机制(详细讲解)
|
6天前
|
算法 Java Python
请简述Python中的垃圾回收机制。
请简述Python中的垃圾回收机制。
15 0
|
6天前
|
算法 Java Python
|
6天前
|
算法 Java 程序员
Python 基础知识: 解释 Python 的垃圾回收机制是如何工作的?
Python 基础知识: 解释 Python 的垃圾回收机制是如何工作的?
|
6天前
|
算法 Java 程序员
|
3天前
|
存储 Java 程序员
【Python 的内存管理机制专栏】深入解析 Python 的内存管理机制:从变量到垃圾回收
【5月更文挑战第18天】Python内存管理关乎程序性能与稳定性,包括变量存储和垃圾回收。变量存储时,如`x = 10`,`x`指向内存中值的引用。垃圾回收通过引用计数自动回收无引用对象,防止内存泄漏。了解此机制可优化内存使用,避免循环引用等问题,提升程序效率和稳定性。深入学习内存管理对成为优秀Python程序员至关重要。
【Python 的内存管理机制专栏】深入解析 Python 的内存管理机制:从变量到垃圾回收
|
6天前
|
监控 算法 Java
Python内存管理与垃圾回收机制
【5月更文挑战第12天】了解Python内存管理与垃圾回收对编写高效稳定程序至关重要。Python自动管理内存,使用`malloc()`和`free()`分配和释放。引用计数跟踪对象引用,当引用计数为零时对象销毁。垃圾回收处理循环引用,采用分代回收算法。优化技巧包括避免循环引用、显式释放对象、使用生成器和迭代器。理解这些机制有助于避免内存泄漏,提高性能。通过示例代码,学习如何在实践中应用内存管理最佳实践和高级优化技巧,以及如何调试和诊断内存问题。在并发和异步编程中,需注意线程安全和异步内存管理。掌握这些知识能提升Python编程的效率和质量。
17 3
|
6天前
|
算法 Java C++
python垃圾回收机制详解
python垃圾回收机制详解
15 0
|
6天前
|
Python
Python类定义:从小白到专家的旅程
Python类定义:从小白到专家的旅程
9 0
|
6天前
|
算法 Java 开发者
详解python中的垃圾回收机制
详解python中的垃圾回收机制
52 0