python 垃圾回收 GC

一. 小整数对象池(常驻内存)

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

  2. Python对小整数的定义是[-5,257) 左闭右开 这些整数对象是提前建立好的,不会被回收,在一个Python程序中,所有位于这个范围内的整数使用的都是同一个对象。同理,单个字母也是这样的,但是当定义2个相同的字符串时,引用计数为0,触发垃圾回收。


Python 2.7.12 (default, Nov 20 2017, 18:23:56) 
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 
>>> 
>>> a = 257
>>> id(a)
29487032
>>> b = 257
>>> id(b)
29487008
>>> c = 256
>>> id(c)
29157408
>>> d = 256
>>> id(d)
29157408
>>> 


Python 3.5.2 (default, Nov 23 2017, 16:37:01) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a = 256
>>> id(a)
10927584
>>> b = 256
>>> id(b)
10927584
>>> 
>>> c = 257
>>> id(c)
140325497813392
>>> d = 257
>>> id(d)
140325497336688
>>> 

二、大整数对象池(用时创建)

  1. 每一个整数,均创建一个新的对象
>>> b = 12345
>>> id(b)
140325497336720
>>> a = 12345
>>> id(a)
140325497336784
>>> type(a)
<class 'int'>
>>> a = b
>>> id(a)
140325497336720
>>> 

三、intern 机制(连续字符)

  1. python 只占用一个「hello」所占的内存空间
  2. 靠引用计数去维护何时释放
>>> a = 'hello'
>>> b = 'hello'
>>> c = 'hello'
>>> d = 'hello'
>>> 
>>> print(id(a))
140325496993696
>>> print(id(b))
140325496993696
>>> print(id(c))
140325496993696
>>> print(id(d))
140325496993696
>>> 

>>> a = 'hello world'
>>> b = 'hello world'
>>> c = 'hello world'
>>> d = 'hello world'
>>> 
>>> 
>>> print(id(a))
140325497036848
>>> print(id(b))
140325497036912
>>> print(id(c))
140325497036976
>>> print(id(d))
140325497037040
>>> 

四、总结

  1. 小整数[-5,257)公用对象,常驻内存
  2. 单个字符共用对象,常驻内存
  3. 单个单词,不可修改,默认开启intern机制,共用对象,引用计数为0销毁
  4. 含有空格的字符串,不可修改,没开启intern机制,不共用对象,应用计数为0,销毁

五、Garbage collection

  1. java、c# 都采用了垃圾收集机制,而不再是像c,c++自己管理维护内存的方式,自己管理内存,存在风险,内存泄露,悬空指针
  2. Python中采用的是引用计数机制为主,标记-清除和分代收集两种机制为辅的策略

(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的时候,改对象生命周期结束


  • 引用计数机制的优点

简单,实时性:一旦没有引用,内存就直接释放,不用像其他机制等到特定时机,实时性还带来了一个好处,处理回收内存的时间分摊到了平时
  • 引用计数机制的缺点
    维护引用计数消耗资源,能搞定大部分内存回收,但是循环引用无法回收(内存泄露)–>> 引入新的回收机制
  • <!--循环引用-->
    
    class Test:
        pass
    
    a = Test()
    b = Test()
    
    a.r = b
    b.r = a
    
    del a
    del b
    
    引用计数各位1
    
    lis = []
    lis_1 = []
    
    lis.apppend(lis_1)
    lis_1.apppend(lis)

    0、gc常用函数

    • gc.set_debug(gc.DEBUG_LEAK),设置gc的debug日志,
    • gc.collect([Generation])显示进行垃圾回收,可以输入参数,0代表只检查一代的对象,1带面检查一二代的对象,2代表检查,1,2,3代的对先发,如果不传参,知心给一个full collection,也就是等于传2。返回不可达对象的数目
    • gc.get_threshold() 获取的gc模块中自动执行垃圾回收的频率
    • gc.set_threshold()
      Docstring:
      set_threshold(threshold0, [threshold1, threshold2]) -> None
    • gc.get_count() 获取当前自动执行垃圾回收的计数器,返回一个长度为3的列表

    1、导致引用计数+1的情况

    • 对象被创建,例如a=2
    • 对象被引用,b=a
    • 对象被作为参数,传入到一个函数中
    • 对象作为一个元素,存储在容器中

    2、导致引用计数-1的情况

    • 对象别名被显示销毁 del
    • 对象别名被赋予新的对象
    • 一个对象离开他的作用域
    • 对象所在的容器被销毁或者是从容器中删除对象

    3、查看对象的引用计数

    sys.getrefcount(a)

    猜你喜欢

    转载自blog.csdn.net/qq_29719097/article/details/79022245