【python进阶】Garbage collection垃圾回收1(上)

简介: 【python进阶】Garbage collection垃圾回收1

前言

GC垃圾回收在python中是很重要的一部分,同样我将分两次去讲解Garbage collection垃圾回收,此篇为Garbage collection垃圾回收第一篇,下面开始今天的说明~~~

1.Garbage collection(GC垃圾回收)

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

引⽤计数机制:

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++)    //增加计数 
#defien Py_DECREF(op) \ //减少计数
    if(--(op)->ob_refcnt!=0)\
        ;\
    else \
        _Py_Dealloc((PyObject *)(op))

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

引⽤计数机制的优点:

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

引⽤计数机制的缺点:

  • 维护引⽤计数消耗资源
  • 循环引⽤
list1 = []
list2 = []
list1.append(list2)
list2.append(list1)

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


2.画说Ruby与Python垃圾回收

英⽂原⽂: visualizing garbage collection in ruby and python

2.1.应⽤程序那颗跃动的⼼

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

  • 为新⽣成的对象分配内存
  • 识别那些垃圾对象
  • 从垃圾对象那回收内存

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

我认为垃圾回收就是应⽤程序那颗跃动的⼼。像⼼脏为身体其他器官提供⾎ 液和营养物那样,垃圾回收器为你的应该程序提供内存和对象。如果⼼脏停跳,过不了⼏秒钟⼈就完了。如果垃圾回收器停⽌⼯作或运⾏迟缓,像动脉阻塞,你的应⽤程序效率也会下降,直⾄最终死掉。

2.2.一个简单的例子

运⽤实例⼀贯有助于理论的理解。下⾯是⼀个简单类,分别⽤Python和Ruby写成,我们今天就以此为例:


1100338-20180426170814943-197959855.png

顺便提⼀句,两种语⾔的代码竟能如此相像:Ruby和Python在表达同⼀事物上真的只是略有不同。但是在这两种语⾔的内部实现上是否也如此相似呢?

2.3.Ruby的对象分配

当我们执⾏上⾯的Node.new(1)时,Ruby到底做了什么?Ruby是如何为我们 创建新的对象的呢? 出乎意料的是它做的⾮常少。实际上,早在代码开始执⾏前,Ruby就提前创建了成百上千个对象,并把它们串在链表上,名⽈:可⽤列表。下图所示为可⽤列表的概念图:

1100338-20180426171112752-2867736.png


想象⼀下每个⽩⾊⽅格上都标着⼀个"未使⽤预创建对象"。当我们调⽤ Node.new ,Ruby只需取⼀个预创建对象给我们使⽤即可:

1100338-20180426171208534-1880528104.png

上图中左侧灰格表示我们代码中使⽤的当前对象,同时其他⽩格是未使⽤对象。(请注意:⽆疑我的示意图是对实际的简化。实际上,Ruby会⽤另⼀个 对象来装载字符串"ABC",另⼀个对象装载Node类定义,还有⼀个对象装载了代码中分析出的抽象语法树,等等)

如果我们再次调⽤ Node.new,Ruby将递给我们另⼀个对象:


1100338-20180426171332549-296648299.png


这个简单的⽤链表来预分配对象的算法已经发明了超过50年,⽽发明⼈这是 赫赫有名的计算机科学家John McCarthy,⼀开始是⽤Lisp实现的。Lisp不仅 是最早的函数式编程语⾔,在计算机科学领域也有许多创举。其⼀就是利⽤垃圾回收机制⾃动化进⾏程序内存管理的概念。

标准版的Ruby,也就是众所周知的"Matz's Ruby Interpreter"(MRI),所使⽤的 GC算法与McCarthy在1960年的实现⽅式很类似。⽆论好坏,Ruby的垃圾回 收机制已经53岁⾼龄了。像Lisp⼀样,Ruby预先创建⼀些对象,然后在你分配新对象或者变量的时候供你使⽤。

2.4.Python的对象分配


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

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

例如我们⽤Pyhon来创建⼀个Node对象:

1100338-20180426171512536-546407773.png


与Ruby不同,当创建对象时Python⽴即向操作系统请求内存.(Python实际 上实现了⼀套⾃⼰的内存分配系统,在操作系统堆之上提供了⼀个抽象层。 但是我今天不展开说了)

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


1100338-20180426171637181-706463974.png


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

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

1100338-20180426171735288-1270285865.png

Ruby把⽆⽤的对象留在内存⾥,直到下⼀次GC执⾏

回过来看Ruby。随着我们创建越来越多的对象,Ruby会持续寻可⽤列表⾥ 取预创建对象给我们。因此,可⽤列表会逐渐变短:


1100338-20180426171804189-1368704003.png


..然后更短:


1100338-20180426171826880-1712642042.png


请注意我⼀直在为变量n1赋新值,Ruby把旧值留在原处。"ABC","JKL"和"MNO"三个Node实例还滞留在内存中。Ruby不会⽴即清除代码中不再使⽤的旧对象!Ruby开发者们就像是住在⼀间凌乱的房间,地板上摞着⾐服,要么洗碗池⾥都是脏盘⼦。作为⼀个Ruby程序员,⽆⽤的垃圾对象会⼀直环绕着你。


2.6.Python开发者住在卫生之家庭


1100338-20180426172036633-558235142.png


⽤完的垃圾对象会⽴即被Python打扫⼲净

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

1100338-20180426172103160-327176772.png


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


1100338-20180426172125041-656951064.png


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


1100338-20180426172146326-1698935265.png


与之前⼀样,Python设置JKL的引⽤数为1。然⽽,请注意由于我们改变了n1 指向了JKL,不再指向ABC,Python就把ABC的引⽤数置为0了。 此刻, Python垃圾回收器⽴刻挺身⽽出!每当对象的引⽤数减为0,Python⽴即将其释放,把内存还给操作系统:


1100338-20180426172211685-1460395998.png


上⾯Python回收了ABC Node实例使⽤的内存。记住,Ruby弃旧对象原地于不顾,也不释放它们的内存。

Python的这种垃圾回收算法被称为引⽤计数。是George-Collins在1960年发明的,恰巧与John McCarthy发明的可⽤列表算法在同⼀年出现。就像MikeBernstein在6⽉份哥谭市Ruby⼤会杰出的垃圾回收机制演讲中说的: "1960年是垃圾收集器的⻩⾦年代..."。

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

现在来看第⼆例⼦。加⼊我们让n2引⽤n1


1100338-20180426172356887-1424942464.png


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

目录
相关文章
|
1月前
|
算法 Java Python
请简述Python中的垃圾回收机制。
请简述Python中的垃圾回收机制。
14 0
|
3月前
|
算法 Java Python
|
3月前
|
机器学习/深度学习 自然语言处理 算法框架/工具
在Python中进行自然语言处理(NLP)的进阶应用
在Python中进行自然语言处理(NLP)的进阶应用
43 3
|
10天前
|
Python
python面型对象编程进阶(继承、多态、私有化、异常捕获、类属性和类方法)(上)
python面型对象编程进阶(继承、多态、私有化、异常捕获、类属性和类方法)(上)
52 0
|
1月前
|
Python
探索Python集合推导式的进阶应用
探索Python集合推导式的进阶应用
|
1月前
|
算法 Java 开发者
详解python中的垃圾回收机制
详解python中的垃圾回收机制
37 0
|
1月前
|
Java 程序员 Python
|
3月前
|
Python Windows
【Python进阶必备】一文掌握re库:实战正则表达式
【Python进阶必备】一文掌握re库:实战正则表达式
79 0
|
3月前
|
Java Shell 程序员
Python 进阶指南(编程轻松进阶):十七、Python 风格 OOP:属性和魔术方法
Python 进阶指南(编程轻松进阶):十七、Python 风格 OOP:属性和魔术方法
27 0
|
3月前
|
存储 小程序 Java
Python 进阶指南(编程轻松进阶):十五、面向对象编程和类
Python 进阶指南(编程轻松进阶):十五、面向对象编程和类
31 0