zoukankan      html  css  js  c++  java
  • Python垃圾回收机制

    python垃圾回收机制和内存管理机制

    引用计数为主, 标记清除和分代回收为辅+缓存机制

    1.引用计数器

    1.1 环状的双线链表 refchain

    ​ 在python程序中创建的任何对象都会存在refchain双向链表中

        name = 'ds'
        age = 18
     	hobby = ['篮球','美女']
    
    # str 
    内部会创建一个结构体数据 [上一个对象(指针),下一个对象(指针),类型,引用计数]
    # name = 'ds'
    new = name   new 不会重新创建一个ds的数据, 是指向ds这个数据,引用计数则加+1
    
    
    # float 多一个val , 存储数据具体值
    内部会创建一个结构体数据 [上一个对象(指针),下一个对象(指针),类型,引用计数,val=18]
    age = 18 
    
    # list , 多存一个items存放的元素容器 和元素个数
    内部会创建一个结构体数据 [上一个对象(指针),下一个对象(指针),类型,引用计数,items=元素,元素个数]
    hobby = ['篮球','美女']
    
    
    

    ​ 在C源码中如何体现每个对象中有相同的值 : PyObject结构体(4个值)

    ​ 有多个元素组成的对象:PyObject结构体(4个值)+ob_size(元素个数)

    # C 源码. 
    PyObject_VAR_HEAD
    // 宏定义 , 包含上一个,下一个,用户构建双向链表,(放到refchain链表中)
    #define _PyObject_HEAD_EXTRA           
        struct _object *_ob_next;   // 下一个对象         
        struct _object *_ob_prev;   // 上一个对象
    
    typedef struct _object {
        _PyObject_HEAD_EXTRA       // 用于构造双向链表
        Py_ssize_t ob_refcnt;      // 引用计数
        struct _typeobject *ob_type;  // 类型
    } PyObject;
    
    typedef struct {
        PyObject ob_base;    //PyObject 对象
        Py_ssize_t ob_size; /* Number of items in variable part  元素的个数*/
    } PyVarObject;
    
    1.2 类型封装的结构
    • float 类型

      typedef struct {
          PyObject_HEAD
          double ob_fval;   // 存放 具体数据
      } PyFloatObject;
      
      // 举例子
      data = 3.14
          _ob_next  : refchain 双向链表中的上一个对象
          _ob_prev  : refchain 双线链表中的下一个对象
          ob_refcnt : 1  // 引用计数
          ob_type	  : float  //类型
          ob_fval   : 3.14
      
    • int 类型

      struct _longobject {
      	PyObject_VAR_HEAD
      	digit ob_digit[1]; 
      };
      
    • list 类型

      typedef struct {
          PyObject_VAR_HEAD
          /* Vector of pointers to list elements.  list[0] is ob_item[0], etc. */
          PyObject **ob_item;   // 元素列表
      
          /* ob_item contains space for 'allocated' elements.  The number
           * currently in use is ob_size.
           * Invariants:
           *     0 <= ob_size <= allocated
           *     len(list) == ob_size
           *     ob_item == NULL implies ob_size == allocated == 0
           * list.sort() temporarily sets allocated to -1 to detect mutations.
           *
           * Items must normally not be NULL, except during construction when
           * the list is not yet visible outside the function that builds it.
           */
          Py_ssize_t allocated;  // 元素个数
      } PyListObject;
      
    • tuple 类型

      typedef struct {
          PyObject_VAR_HEAD
          PyObject *ob_item[1];
      
          /* ob_item contains space for 'ob_size' elements.
           * Items must normally not be NULL, except during construction when
           * the tuple is not yet visible outside the function that builds it.
           */
      } PyTupleObject;
      
    • dict 类型

      typedef struct {
          PyObject_HEAD
      
          /* Number of items in the dictionary */
          Py_ssize_t ma_used;
      
          /* Dictionary version: globally unique, value change each time
             the dictionary is modified */
          uint64_t ma_version_tag;
      
          PyDictKeysObject *ma_keys;
      
          /* If ma_values is NULL, the table is "combined": keys and values
             are stored in ma_keys.
      
             If ma_values is not NULL, the table is splitted:
             keys are stored in ma_keys and values are stored in ma_values */
          PyObject **ma_values;
      } PyDictObject;
      
    1.3 引用计数器
    v1 = 3.14
    v2 = 1
    v3 = (1,2,3)
    

    在python程序运行过程中,会根据数据类型的不同找到其对应的结构体. 根据结构体中的字段来创建相关的数据,然后将对象添加到refchain双线链表中

    在C源码中,有两个关键的结构体: PyObject , PyVarObject

    每个对象中都有ob_refcnt, 这就是引用计数器.默认值是1,当其他变量引用对象时,引用计数器就会发生变化

    • 引用

      a = 999
      b = a
      
      # 999 对象 的引用计数器就会 +1
      
    • 删除

      a = 999
      b = a
      
      del b   # 1. b变量删除, 2. b对应的对象的引用计数器 -1 
      del a   # 1. a变量删除, 2. a对应的对象的引用计数器 -1 , 就是 0 
      
      # 当一个对象的引用计数器 为 0 时, 意味着没有再使用这个变量, 这个变量就是垃圾. 垃圾就要进行垃圾回收机制
      
      # 垃圾回收,两件事 :
      	1. 对象从refchain双线链表中移除
      	2. 将对象销毁,内存归还.
      
    1.4 循环引用问题
    v1 = [1,2,3]
    v2 = [4,5,6]
    v1.append(v2)    // v2 追加到v1列表中, v2的引用计数器+1,最终为2
    v2.append(v1)	// v1 追加到v2列表中, v1的引用计数器+1,最终为2
    
    
    del v1   // 删除v1 变量,引用计数器-1
    del v2  // 删除v2 变量,引用计数器-1
    
    # 问题: 
    	此时, v1和v2的引用计数器还是1,不是0,所以不会被当做垃圾给回收?
        `标记清除` : 解决这样的问题
    

    2.标记清除

    目的: 为了解决引用计数器循环引用的不足

    实现:在python的底层,在维护一个链表,链表中专门存放那些可能存在循环引用的对象(list/set/tuple)

    ​ 在Python内部某种情况下触发,会扫描可能存在循环引用的链表中的每一个元素,检查是否有循环引用,如果有则让双方引用计数器 -1 ;如果是0则垃圾回收

    3.分代回收

    将肯能存在循环引用的对象维护成3个链表

    • 0 代 : 0代中对象个数达到700个扫描一次
    • 1 代 : 0代扫描10次,则 1代扫描一次
    • 2 代 : 1代扫描10次,则 2代扫描一次

    4.小结

    在python中维护了一个refchain的双向环状链表, 这个链表中存储了程序中创建的所有对象,每种类型的对象中都有一个 ob_refcnt引用计数器的值,引用个数+1 , -1 , 最后当引用计数器变为0时,会进行垃圾回收(对象销毁,refchain中移除)

    但是,在python中对于那些可以有多个元素组成的对象,可能会存在循环引用的问题,为了解决这个问题python又引用了标记清除分代回收

    ,在python内部维护了4个链表:

    • refchain
    • 2代 10次
    • 1代 10次
    • 0代 700个对象

    在python源码内部,当达到各自的阈值时,就会触发扫描链表进行标记清除的动作(如果有循环引用,则各自-1引用)

    但是:源码内部在上述的流程中提出了优化机制缓存

    5.python缓存

    5.1 小数据池(int , str字符串)

    ​ 为了避免重复的创建和销毁一些常见对象,维护了一个池.

    v1 = 7
    v2 = 9
    v3 = 9
    
    # python 为了节省资源
        1. 在启动解释器的时候, 内部会创建`-5,-4,....256`范围数值
        2. 在内部 v1 =7 不会创建新的新对象,也不会开辟内存.直接上池中去取数据
    
        # print(id(v2),id(v3))
    
    	3.超过 -5 ... 256 这个范围,重新开辟内存,创建对象 
    
    5.2 free_list(float:100/tuple:20/dict:80/list:80)

    ​ 当引用计数器为0时,按理说是要进行垃圾回收. python内部不会去回收,而是将对象添加到free_list这个链表中作为缓存. 以后再去创建对象时,不会再去开辟内存,直接使用free_list中的数据

    v1 = 3.14  # 1. 开辟内存,创建对象,引用计数器为1,并添加到refchain链表中
    
    del v1     # 2. 从refchain中移除 , 将这个对象添加到free_list中,如果free_list链表缓存满了,再进行垃圾回收
    
    v9 = 999.99 # 3. 不会重新开辟内存,直接去free_list中获取一个float类型的数据,对象内部数据初始化,再放到refchain链表中
    
    
    银角大王笔记:https://pythonav.com/wiki/detail/6/88/
  • 相关阅读:
    Docker私服仓库push 错误
    linux maven安装配置
    centos下载jdk
    Java MVC Controller 中通过不同方式获取 @PathVariable 参数值
    鼠标滑过显示子类浮层
    js如何判断用户是否是用微信浏览器
    纯C语言实现线性链表
    纯C语言实现线性表
    读书笔记 计算机系统--系统架构与操作系统的高度集成 第四章中断、陷入及异常
    读书笔记 计算机系统--系统架构与操作系统的高度集成 第三章处理器实现
  • 原文地址:https://www.cnblogs.com/dengz/p/14653788.html
Copyright © 2011-2022 走看看