Python垃圾回收,搞定面试

简介: 一句话: Pthon的垃圾回收机制使用的是引用计数为主,清楚标记算法和分代为辅

Python垃圾回收机制


1 引用计数:


Python采用引用计数的方式对对象进行引用管理,源码中Obj_ref变量为被引用次数,当有此对象的引用存在的时候,obj_ref-1, 反之+1,当obj_ref减为0时, 该对象立即被回收,对象占用的内存空间将被释放。引用计数机制所带来的维护引用计数的额外操作与Python运行中所进行的内存分配和释放,引用赋值次数是成正比的,这也是引用计数执行效率比较慢的原因。Python中使用很多内存池来进行不同对象的管理和优化。这里面还会存在循环引用的问题,当若干对象相互引用,会造成每个对象的引用计数都不会为0,这样这些对象所占用的内存永远不会被回收。


双向环状链表-Refchain:Python底层维护了一个双向循环链表RefChain,每当创建一个新对象的时候就会将对象添加到这个链表中。在refChain内部,每个对象都有一个ob_refcnt来记录当前对象被引用的次数

0.png

refChain中Object的定义

define PyObject_HEAD       PyObject ob_base;
#define PyObject_VAR_HEAD      PyVarObject ob_base;
// 宏定义,包含 上一个、下一个,用于构造双向链表用。(放到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;

C语言底层定义的两种结构,一种是PyObject 和PyVarObject。 当创建值类型数据的时候,创建PyObject(ob_next, ob_prev, ob_refcnt, type)4个值。 当创建像list,dict,tuple和set的时候,便创建PyVarObject(ob_next, ob_prev, ob_refcnt, type, ob_size)在值类型数据的基础上添加一个元素个数, 就是5个值。


问题


当你创建了list1, list2。list1中有一个引用时list2, list2中有一个引用是list1。这时候就会发生循环引用的问题,导致最后del两个list了但是他们的引用对象计数在refChain中还是1,永远不会被清楚,内存一直被占据着。


清除标记算法:


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


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


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


使用此种方法无法解决循环引用的问题,所以引入标记清楚算法(Mark_Sweep)。GC会对所有对象打上标记,分为活动对象和非活动对象。Python中通过一个directed graph进行对所有对象的标记维护。沿着途中的所有引用,从root object遍历,能到达的对象为活动对象,无法到达的对象为非活动对象。GC首先会把这些标记为非活动对象的object回收

python 通过ref_count计算应用计数垃圾回收,gc_ref是ref_count副本,用于循环应用的垃圾回收,GC时分两步,一是gc_ref自减1,摘除循环引用;二是从root根出发标记是否可达。最后gc_ref为0且不可达将被回收

问题:


1.什么时候扫描


2.可能存在循环引用的链表扫描代价大, 每次扫描耗时久


生命代, 分代回收:


将可能存在循环应用的对象维护成3个链表:


0代: 0代中的对象个数达到700个扫描一次。


1代:0代扫描10次,则1代扫描一次


2代:1代扫描10次, 则2代扫描一次


Python中还会使用分代回收。Python将所有的对象的存活时间分为三个集合,0代(新代),1代(中年代),2代(老年代)。三个代分别指向3个不同的链表,对象的存活时间越长,GC回收对象的频率就越低。新创建的对象都会分配在年轻代,年轻代链表的总数达到上限时,Python垃圾回收机制就会被触发,把那些可以被回收的对象回收掉, 那些不会回收的对象就会被一道中年代去。以此类推,老年代中的对象是存活时间最久的对象,甚至是存活于整个系统的生命周期。当对象数量为700时进行一次年轻代回收,当进行10次年轻代回收后进行一次中年代回收。


Python缓存


池(int, str)


目的:为了避免重复创建和销毁一些常见对象,维护池 var < 257的时候,直接从缓存池中取int。 > 257的时候就会创建一个新的对象,开辟新的内存空间。


free List(float/list/tuple/dict)


当一个对象的引用计数器为0的时候,内部不会直接回收,二十直接将对西昂添加到free_list 链表中当缓存。当再去创建兑现过的时候,不会再重新开辟内存,而是直接使用free_list


常见类型的struct


float 类型

typedef struct {
      PyObject_HEAD
      double ob_fval;
  } PyFloatObject;

int类型

struct _longobject {
      PyObject_VAR_HEAD
      digit ob_digit[1];
  };
  /* Long (arbitrary precision) integer object interface */
  typedef struct _longobject PyLongObject; /* Revealed in longintrepr.h */

str类型

typedef struct {
      PyObject_HEAD
      Py_ssize_t length;          /* Number of code points in the string */
      Py_hash_t hash;             /* Hash value; -1 if not set */
      struct {
          unsigned int interned:2;
          /* Character size:
         - PyUnicode_WCHAR_KIND (0):
           * character type = wchar_t (16 or 32 bits, depending on the
             platform)
         - PyUnicode_1BYTE_KIND (1):
           * character type = Py_UCS1 (8 bits, unsigned)
           * all characters are in the range U+0000-U+00FF (latin1)
           * if ascii is set, all characters are in the range U+0000-U+007F
             (ASCII), otherwise at least one character is in the range
             U+0080-U+00FF
         - PyUnicode_2BYTE_KIND (2):
           * character type = Py_UCS2 (16 bits, unsigned)
           * all characters are in the range U+0000-U+FFFF (BMP)
           * at least one character is in the range U+0100-U+FFFF
         - PyUnicode_4BYTE_KIND (4):
           * character type = Py_UCS4 (32 bits, unsigned)
           * all characters are in the range U+0000-U+10FFFF
           * at least one character is in the range U+10000-U+10FFFF
         */
          unsigned int kind:3;
          unsigned int compact:1;
          unsigned int ascii:1;
          unsigned int ready:1;
          unsigned int :24;
      } state;
      wchar_t *wstr;              /* wchar_t representation (null-terminated) */
  } PyASCIIObject;
  typedef struct {
      PyASCIIObject _base;
      Py_ssize_t utf8_length;     /* Number of bytes in utf8, excluding the
                                   * terminating \0. */
      char *utf8;                 /* UTF-8 representation (null-terminated) */
      Py_ssize_t wstr_length;     /* Number of code points in wstr, possible
                                   * surrogates count as two code points. */
  } PyCompactUnicodeObject;
  typedef struct {
      PyCompactUnicodeObject _base;
      union {
          void *any;
          Py_UCS1 *latin1;
          Py_UCS2 *ucs2;
          Py_UCS4 *ucs4;
      } data;                     /* Canonical, smallest-form Unicode buffer */
  } PyUnicodeObject;

list类型

typedef struct {
      PyObject_VAR_HEAD
      PyObject **ob_item;
      Py_ssize_t allocated;
  } PyListObject;

tuple类型

typedef struct {
      PyObject_VAR_HEAD
      PyObject *ob_item[1];
  } PyTupleObject;

list 类型

typedef struct {
      PyObject_HEAD
      Py_ssize_t ma_used;
      PyDictKeysObject *ma_keys;
      PyObject **ma_values;
  } PyDictObject;
相关文章
|
2天前
|
SQL 分布式计算 前端开发
10个常见的python面试问题_python面试常见问题
10个常见的python面试问题_python面试常见问题
|
2天前
|
存储 数据可视化 算法
最新Python-Matplotlib可视化(9)——精通更多实用图形的绘制,2024年最新小米面试题库
最新Python-Matplotlib可视化(9)——精通更多实用图形的绘制,2024年最新小米面试题库
最新Python-Matplotlib可视化(9)——精通更多实用图形的绘制,2024年最新小米面试题库
|
2天前
|
数据采集 Java 数据挖掘
最新Python+OpenCV+dlib汽车驾驶员疲劳驾驶检测!,2024年最新网易云java面试
最新Python+OpenCV+dlib汽车驾驶员疲劳驾驶检测!,2024年最新网易云java面试
最新Python+OpenCV+dlib汽车驾驶员疲劳驾驶检测!,2024年最新网易云java面试
|
2天前
|
数据采集 算法 网络协议
最新Python 面试常见问题(1),2024年最新面试官必问的10个问题
最新Python 面试常见问题(1),2024年最新面试官必问的10个问题
最新Python 面试常见问题(1),2024年最新面试官必问的10个问题
|
2天前
|
数据采集 XML 程序员
最新用Python做垃圾分类_python垃圾分类代码用key和format,5年经验Python程序员面试27天
最新用Python做垃圾分类_python垃圾分类代码用key和format,5年经验Python程序员面试27天
最新用Python做垃圾分类_python垃圾分类代码用key和format,5年经验Python程序员面试27天
|
2天前
|
Python
最新用Python做一个变态版的《超级玛丽》游戏,面试必备知识点
最新用Python做一个变态版的《超级玛丽》游戏,面试必备知识点
最新用Python做一个变态版的《超级玛丽》游戏,面试必备知识点
|
2天前
|
数据采集 机器学习/深度学习 人工智能
最新用python代码画爱心,来自程序猿的浪漫~_python画爱心代码(1),2024年最新面试简历模板免费
最新用python代码画爱心,来自程序猿的浪漫~_python画爱心代码(1),2024年最新面试简历模板免费
最新用python代码画爱心,来自程序猿的浪漫~_python画爱心代码(1),2024年最新面试简历模板免费
|
2天前
|
区块链 Python
最新用Python从零开始创建区块链_基于python做区块链,哔哩哔哩测试面试题
最新用Python从零开始创建区块链_基于python做区块链,哔哩哔哩测试面试题
|
2天前
|
存储 机器学习/深度学习 数据安全/隐私保护
最全Pillow(PIL)入门教程(非常详细)_python pillow 教程,2024年最新Python面试送分题
最全Pillow(PIL)入门教程(非常详细)_python pillow 教程,2024年最新Python面试送分题
最全Pillow(PIL)入门教程(非常详细)_python pillow 教程,2024年最新Python面试送分题
|
2天前
|
架构师 数据挖掘 Python
最全pandas库(Python),2024年最新阿里云架构师面试
最全pandas库(Python),2024年最新阿里云架构师面试
最全pandas库(Python),2024年最新阿里云架构师面试