xone

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

 

垃圾回收
1. 小整数对象池
整数在程序中的使用非常广泛,Python为了优化速度,使用了小整数对象池,避免为整数频繁申请和销毁内存空间。

Python 对小整数的定义是 [-5, 257) 这些整数对象是提前建立好的,不会被垃圾回收。在一个Python 的程序中,所有位于这个范围内的整数使用的都是同一个对象.

同理,单个字母也是这样的。

但是当定义2个相同的字符串时,引用计数为0,触发垃圾回收

2. 大整数对象池
每一个大整数,均创建一个新的对象。

In [29]: id(b)
Out[29]: 10914368

In [30]: a=1000

In [31]: b=1000

In [32]: id(a)
Out[32]: 139681389577584

In [33]: id(b)
Out[33]: 139681389577712

  

3. 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”所占的内存空间。靠引用计数去维护何时释放。

 

总结
小整数[-5,257)共用对象,常驻内存
单个字符共用对象,常驻内存
单个单词,不可修改,默认开启intern机制,共用对象,引用计数为0,则销毁 单词垃圾回收
字符串(含有空格),不可修改,没开启intern机制,不共用对象,引用计数为0,销毁
大整数不共用内存,引用计数为0,销毁 大整数垃圾回收
数值类型和字符串类型在 Python 中都是不可变的,这意味着你无法修改这个对象的值,每次对变量的修改,实际上是创建一个新的对象不可变

 

循环引用并且关闭gc,内存不断增加

import gc

class Test1(object):
    def __init__(self):
        print("object born,id:%s"%str(hex(id(self))))

def f2():
    while True:
        c1 = Test1()
        c2 = Test1()
        c1.t = c2
        c2.t = c1
        del c1
        del c2
gc.disable()
f2()

  

 

python采用的是引用计数为主,隔代回收为辅的垃圾回收策略

GC系统承担的工作远比“垃圾回收”多得多。

主要负责三个任务:

1、为新生成的对象分配内存

2、识别那些垃圾对象

3、从垃圾对象那回收内存

 

分代回收思想将对象分为三代(generation 0,1,2),0代表幼年对象,1代表青年对象,2代表老年对象。根据弱代假说(越年轻的对象越容易死掉,老的对象通常会存活更久。)
新生的对象被放入0代,如果该对象在第0代的一次gc垃圾回收中活了下来,那么它就被放到第1代里面(它就升级了)。如果第1代里面的对象在第1代的一次gc垃圾回收中活了下来,它就被放到第2代里面。gc.set_threshold(threshold0[,threshold1[,threshold2]])设置gc每一代垃圾回收所触发的阈值。从上一次第0代gc后,如果分配对象的个数减去释放对象的个数大于threshold0,那么就会对第0代中的对象进行gc垃圾回收检查。 从上一次第1代gc后,如果第0代被gc垃圾回收的次数大于threshold1,那么就会对第1代中的对象进行gc垃圾回收检查。同样,从上一次第2代gc后,如过第1代被gc垃圾回收的次数大于threshold2,那么就会对第2代中的对象进行gc垃圾回收检查。

 

gc模块常用功能

gc模块提供一个接口给开发者设置垃圾回收的选项。上面说到,采用引用计数的方法管理内存的一个缺陷是循环引用,而gc模块的一个主要功能就是解决循环引用的问题。

###常用函数
1、gc.set_debug(flags)设置gc的debug日志,一般设置为gc.DEBUG_LEAK

2、gc.collect([generation]) 显式进行垃圾回收,可以输入参数,0代表只检查第一代的对象,1代表检查一,二代的对象,2代表检查一,二,三代的对象,如果不传参数,执行一个full collection,也就是等于传2。 返回不可达(unreachable objects)对象的数目

3、gc.get_threshold() 获取的gc模块中自动执行垃圾回收的频率。

4、gc.set_threshold(threshold0[, threshold1[, threshold2]) 设置自动执行垃圾回收的频率。

5、gc.get_count() 获取当前自动执行垃圾回收的计数器,返回一个长度为3的列表

 

查看当前分代回收的频率

In [9]: gc.get_threshold()
Out[9]: (700, 10, 10)

  

 

1、导致引用计数+1的情况:
对象被创建,例如a=23
对象被引用,例如b=a
对象被作为参数,传入到一个函数中,例如func(a)
对象作为一个元素,存储在容器中,例如list=[a,a]


2、导致引用计数-1的情况:
对象的别名被显示销毁,例如del a
对象的别名被赋予新的对象,例如a=24
一个对象离开它的作用域,例如f函数执行完毕时,func函数中的局部变量(全局变量不会改变)
对象所在的容器被销毁,或从容器中删除对象

 

 3、查看一个对象的引用计数

In [9]: a = "hello world"

In [10]: sys.getrefcount(a)
Out[10]: 2

  

posted on 2019-01-17 22:25  周小百  阅读(270)  评论(0编辑  收藏  举报