垃圾回收机制(Garbage collection)
一、小整数对象池
整数在程序中的使用非常广泛,Python为了优化速度,使用了小整数对象池, 避免为整数频繁申请和销毁内存空间。
Python 对小整数的定义是 [-5, 257) 这些整数对象是提前建立好的,不会被垃圾回收。在一个 Python 的程序中,所有位于这个范围内的整数使用的都是同一个对象。.
同理,单个字母也是这样的。但是当定义2个相同的字符串时,引用计数为0,将会触发垃圾回收机制。
二、大整数对象池
每一个大整数,均创建一个新的对象。
三、intern机制
a1 = "HelloWorld"
a2 = "HelloWorld"
a3 = "HelloWorld"
a4 = "HelloWorld"
a5 = "HelloWorld"
a6 = "HelloWorld"
a7 = "HelloWorld"
a8 = "HelloWorld"
a9 = "HelloWorld"
python会不会创建9个对象呢?在内存中会不会开辟9个”HelloWorld”的内存空间呢? 想象下,如果是这样的话,我们写10000个对象,比如a1=”HelloWorld”…..a1000=”HelloWorld”, 那他岂不是开辟了1000个”HelloWorld”所占的内存空间了呢?如果真这样,内存不就爆了吗?所以python中有这样一个机制—— intern机制 ,让他只占用一个”HelloWorld”所占的内存空间。靠引用计数去维护何时释放。
小结:
1. 小整数[-5,257)共用对象,常驻内存
2. 单个字符共用对象,常驻内存
3. 单个单词,不可修改,默认开启intern机制,共用对象,如果引用计数为0,则销毁。
注:字符串(含有空格),不可修改,没有开启intern机制,不共享对象,如果引用计数为0,则销毁该对象。
四、垃圾回收机制原理
Python里也同java一样采用了垃圾收集机制,不过不一样的是: Python采用的是引用计数机制为主,分代收集两种机制为辅的策略。
4.1、引用计数机制:
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时,该对象生命就结束了
4.2、引用计数机制的优点:
1、简单
2、实时性:一旦没有引用,内存就直接释放了。不用像其他机制等到特定时机。实时性还带来一个好处:处理回收内存的时间分摊到了平时。
4.3、引用计数机制的缺点:
1、维护引用计数消耗资源
2、循环引用
五、在Ruby和Python中可视化垃圾收集
http://patshaughnessy.net/2013/10/24/visualizing-garbage-collection-in-ruby-and-python#disqus_thread
应用程序的跳动之心
GC系统不仅仅是“收集垃圾”。事实上,它们执行三项重要任务。他们
为新对象分配内存,
识别垃圾对象,和
从垃圾对象中回收内存。
想象一下,如果您的应用程序是一个人体:您编写的所有优雅代码,您的业务逻辑,您的算法,将是应用程序内部的大脑或智能。按照这个比喻,你认为垃圾收集器的身体部位是什么?[我从RuPy观众那里得到了很多有趣的答案:肾脏,白细胞:)]
我认为垃圾收集器是您应用程序的跳动核心。正如您的心脏为身体的其他部分提供血液和营养一样,垃圾收集器为您的应用程序提供内存和对象供您使用。如果你的心脏停止跳动,你会在几秒钟内死亡。如果垃圾收集器停止或运行缓慢 - 如果它堵塞了动脉 - 你的应用程序将减速并最终死亡!
一个简单的例子
使用示例来理解理论总是有帮助的。这是一个用Python和Ruby编写的简单类,我们今天可以用它作为例子:
-
顺便说一句,令我惊讶的是这个代码在两种语言中的相似之处:Ruby和Python实际上只是略有不同的说法。但语言也实现在国内类似的方式?
免费清单
当我们调用上面的Node.new(1)时,Ruby究竟做了什么?Ruby如何为我们创建一个新对象?
令人惊讶的是,它确实很少!事实上,在您的代码开始运行之前很久,Ruby就会提前创建数千个对象并将它们放在一个名为空闲列表的链表上。从概念上讲,这是免费列表的样子:
想象一下,上面的每个白色方块都是一个未使用的,预先创建的Ruby对象。当我们调用Node.new时,Ruby只需要将其中一个对象交给我们:
在上图中,左侧的灰色方块表示我们在代码中使用的活动Ruby对象,而剩余的白色方块是未使用的对象。[注意:当然,我的图表是现实的简化版本。事实上,Ruby会使用另一个对象来保存字符串“ABC”,第三个对象用于保存Node的类定义,还有其他对象用于保存我的代码的解析后的抽象语法树(AST)表示等。
如果我们再次调用Node.new,Ruby只会给我们另一个对象:
在Python中分配对象
我们已经看到Ruby提前创建对象并将它们保存在空闲列表中。那Python怎么样?
虽然Python在内部也出于各种原因使用空闲列表(它会回收某些对象,例如列表),但它通常会为新对象和值分配内存,而不像Ruby那样。
假设我们使用Python创建一个Node对象:
与Ruby不同,Python会在您创建对象时立即向操作系统询问内存。(Python实际上实现了自己的内存分配系统,它在操作系统堆之上提供了额外的抽象层。但是我今天没有时间进入这些细节。)
当我们创建第二个对象时,Python将再次向操作系统询问更多内存:
看起来够简单吧,在我们创建对象的时候,Python会花些时间为我们找到并分配内存。