zoukankan      html  css  js  c++  java
  • Python的内存管理和垃圾回收机制

    前言

     为什么 已经 del 析构了 name 变量,然后新的变量 xxxxx的 内存地址却跟原来name一样呢?

    带着这个疑问看看了Python的内存管理机制。

     

    一、内存管理机制

    Python语言是由C实现的,所以想要剖析Python的内存管理机制,就需要下载Python的源码包看看C源代码是怎么写的?

    C源码有2个关键目录

    Include

    Objects

    1.两个重要的结构体

    #define _PyObject_HEAD_EXTRA            
        struct _object *_ob_next;           
        struct _object *_ob_prev;
         
    #define PyObject_HEAD       PyObject ob_base;
     
    #define PyObject_VAR_HEAD      PyVarObject ob_base;
     
     
    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;
    include/object.h

    Python中所有类型创建对象时,底层都是PyObject和PyVarObject结构体实现,一般情况下由单个元素组成对象内部会使用PyObject结构体(float)、由多个元素组成的对象内部会使用PyVarObject结构体(str/int/list/dict/tuple/set/自定义类),因为由多个元素组成的话是需要为其维护一个 ob_size(内部元素个数)。

    Python 执行 v=0.4
    C语言 1.开辟内存 2.数据初始化
      ob_fval
    =0.3
    ob_type=float
    ob_refcnt=1
    3.将对象放到双向链表中 ref_chain

    Python 执行 name=v
    0.不会重新开辟内存
    1.
    ob_refcnt=1
    Python执行 del v
    ob_refcnt-1

    Python执行
      def fuc(arg):
        print(123)
      func(name)
    1.arg参数刚进去 ob_refcnt+1
    2.fuc执行完  ob_refcnt-1
    Python执行 del name
    1.ob_refcnt-1
    每次ob_refcnt-1都检查是否为0

    如果引用计数器为0就按理说 内存就应该销毁。
    但是Python为了提升效率, 会对Python 某些数据类型做一些缓存机制;
    为了减少开辟内存和销毁内存占用的时间,我们会把引用计数器为0的对象放到双向链表中,方便下次创建float类型时可以继续使用原来的内存地址
    
    

    内存管理概述

    Python是由C语言开发的解释器,任何操作Python都会调用C的代码。

    PyObject: 指向上1个值指针、指向下1个值的指针、计数器、类型

    PyobjectVarObject: PyObject、容器个数

    在Python中每创建个对象,都会由C语言结构体内部都要维护4个值:双向链表、ob_refcnt、ob_type之后对内存中的数据进行初始化。

    引用计数=0, 赋值然后将对象放到双向链表中refchain.

    以后再有其他变量指向该内存 引用计算器+=1,如果销毁某个变量,则找到指向的内存,讲其计数器引用-1

    引用计数器如果引用为0,则进行垃圾回收。

    但是某些数据在内部可能存在缓存机制  例如float/list/int,在其引用计数器引用计数器=0时,不会真正销毁,而是放在 1个叫 free_list的链表中。

    如果后期再创建同类型数据时,会取出链表中的对象,然后对对象进行初始化操作,重新赋上新值。

     二、垃圾回收机制

    Python的垃圾回收机制,以引用计数为主,标记清除、分代回收为辅。 

    1.引用计数

    如果在Python里面创建了1个a=10的变量,那么a变量就指向了对象10。

    Cpython解释器会记录对象10这个对象被变量使用的数量,当这个对象引用数量为0时 被回收。

    2.标记清除

    标记清除策略是对引用计数测试的补充,解决的问题就是当容器数据类型(list、tuple、dict、object)类数据  引用计数为0时,但是它们内部的数据却引用着其他数据。

    我现在变量a、b已经访问不到 对象A、B了,但是A、B对象依然引用着其他数据。所以我需要把对象A、B都回收掉。 

    class A():
        pass
    
    class B():
        pass
    
    a=A()
    b=B()
    a.haha=b
    b.hehe=a
    #a---A
    #b---B
    a=None
    b=None
    
    print(a)

    解决之道

     

    标记清除(Mark—Sweep)算法是一种基于追踪回收(tracing GC)技术实现的垃圾回收算法。它分为两个阶段:第一阶段是标记阶段,GC会把所有的『活动对象』打上标记,第二阶段是把那些没有标记的对象『非活动对象』进行回收。那么GC又是如何判断哪些是活动对象哪些是非活动对象的呢?

    对象之间通过引用(指针)连在一起,构成一个有向图,对象构成这个有向图的节点,而引用关系构成这个有向图的边。从根对象(root object)出发,沿着有向边遍历对象,可达的(reachable)对象标记为活动对象,不可达的对象就是要被清除的非活动对象。根对象就是全局变量、调用栈、寄存器。

    3.分代回收

    如果把Python中声明的所有容器类对象,都放在1个双向链表里进行循环检查 计数器是否为0 ?是否存在循环引用,是不是很耗时呢?

    我们可以先把新创建的变量放在1个地方,如果没有以上3种垃圾回收机制都没有回收掉它----------》就放到另1个地方-------》如果还没有被回收掉就放在------------》 下一个地方。这就是分代管理。

    分配内存
    -> 发现超过阈值了
    -> 触发垃圾回收
    -> 将所有可收集对象链表放到一起
    -> 遍历, 计算有效引用计数
    -> 分成 有效引用计数=0 和 有效引用计数 > 0 两个集合
    -> 大于0的, 放入到更老一代
    -> =0的, 执行回收
    -> 回收遍历容器内的各个元素, 减掉对应元素引用计数(破掉循环引用)
    -> 执行-1的逻辑, 若发现对象引用计数=0, 触发内存回收
    -> python底层内存管理机制回收内存

    解决之道

    分代回收是一种以空间换时间的操作方式,Python将内存根据对象的存活时间划分为不同的集合,每个集合称为一个代,Python将内存分为了3“代”,分别为年轻代(第0代)、中年代(第1代)、老年代(第2代),他们对应的是3个链表,它们的垃圾收集频率与对象的存活时间的增大而减小。新创建的对象都会分配在年轻代,年轻代链表的总数达到上限时,Python垃圾收集机制就会被触发,把那些可以被回收的对象回收掉,而那些不会回收的对象就会被移到中年代去,依此类推,老年代中的对象是存活时间最久的对象,甚至是存活于整个系统的生命周期内。同时,分代回收是建立在标记清除技术基础之上。分代回收同样作为Python的辅助垃圾收集技术处理那些容器对象

    参考

  • 相关阅读:
    Security headers quick reference Learn more about headers that can keep your site safe and quickly look up the most important details.
    Missing dollar riddle
    Where Did the Other Dollar Go, Jeff?
    proteus 与 keil 联调
    cisco router nat
    router dhcp and dns listen
    配置802.1x在交换机的端口验证设置
    ASAv931安装&初始化及ASDM管理
    S5700与Cisco ACS做802.1x认证
    playwright
  • 原文地址:https://www.cnblogs.com/sss4/p/12334308.html
Copyright © 2011-2022 走看看