python内存管理

python垃圾回收

  • 引用计数

    1. 环状双向列表(refchain)

      在python中,任意对象都会放在双向列表里面

      name = 'dream'
      age = 18
      happy = ['Computer Game','Play']
      
      """
      存放的相同的数据
      在内部创建一个结构体 (打包成一个数据)
      存放的数据:
      上一个数据的指针
      下一个数据的指针
      数据的类型
      引用的个数
      """
      # age = 18
      """
      存放你的数据
      不同的:
      val = 18
      """
      
      # happy = ['Computer Game','study']
      """
      存放的数据
      item = 存放的元素
      元素个数
      """
      
      1. 在C源码中PyObject体现出每个对象都有相同的值
      2. 由多个元素组成的的值:PyObject + ob_size :
    2. 类封装结构体

      a = 3.14
      """
      内部会创建
      _ob_next    环状双向列表存放的上一个数据
      _ob_prev	环状双向列表存放的下一个数据
      ob_refcnt = 1    引用个数
      ob_type = Float  对象类型
      ob_val = 3.14	  对象的值
      """
      
    3. 引用计数器

      v1 = 3.14
      v2 = 999
      v3 = (1,2,3)
      

      在python程序运行时,会根据数据类型的不同找到其对应的结构,根据结构中的字段来创建相关的数据,然后添加到双向列表中。在C源码中有两个结构体比较重要:PyObject,PyVarObject

      PyObject:存放的是工共的数据。比如说,上一个数据的指针,下一个数据的指针………

      PyVarOjbect :存放的是多个元素组成的时候,公共的值。

      每个对象在创建的过程中,都有一个ob_refcnt(引用计数):默认是1,当有其他变量引用对象时,引用计数器就会发生变化。

      • 引用
      a = 999 
      b = a 
      # 此时999的引用就会变成2了
      
      • 删除引用

        a = 999
        b = a
        
        del b 
        # 把对象b删除,此时b对象的引用计数器减一
        # 把对象a删除,此时a对象的引用计数器减一
        # 当对象的引用计数器为0时,意味着没有使用这个对象了,这个对象就是垃圾,就会进行垃圾回收
        # 回收:把对象从refchain进行回收,将这个对象从内存销毁,并把内存归还
        
  • 循环引用

    a = ['a','b','c','d']
    b = ['a','b','c','d']
    a.append(b)  # 把列表b添加列表a的末尾 同时b的引用计数器+1
    b.append(a)  # 把列表a添加列表b的末尾 同时a的引用计数器+1
    del a   # 删除列表a 同时a的引用计数器-1
    del b   # 删除列表b 同时b的引用计数器-1
    """
    此时a,b的引用计数都为1
    """
    
    1. 标记清除
      • 目的:为了解决循环引用
      • 实现:在python在去维护一个列表,列表中存放一个可能出现循环引用的对象。
      • 可能出现循环引用的对象
        1. 列表
        2. 字典
        3. 元组
      • 在python情况下***在某种情况下***触发,回去扫描可能出现***循环引用列表***中的每个元素。检查是否有循环引用。如果是则双方的引用计数减一,如果是0则进行垃圾回收。
      • 问题
        1. 什么时候扫描
        2. 代表比较大(比较耗时)
  • 分代回收

    将可能出现循环引用的对象维护成三个链表

    1. 0代 :0代中的对象达到700个扫描一次
    2. 1代 :0代扫描十次,则1代开始扫描
    3. 2代 :1代扫描十次,则2代开始扫描
    v1 = [1,2,3,4,5]
    v2 = [6,7,8,9,0]
    v1.append(v2)
    v2.append(v1)
    """
    第一步:会把v1和v2放在0代的链表里面。
    第二部:当0代的链表里面达到700个数据时会进行扫描。
    第三步:查看里面是否有循环引用的数据,如果有就会把引用次数-1。
    第四步:查看引用次数,如果引用次数为0,就会被回收,如如果不为零就会把里面所有的数据给1代
    """
    
  • 小节

    在python中自己维护了一个refchain双向维护列表,这个链表中存储程序创建的所有对象。每个对象都有一个ob_frecnt引用计数的值。引用计数+1或者-1。当引用计数为0时,就会进行垃圾回收(对象销毁,从refchain移除)

    但是,对于python中可以有多个元素可能出现循环引用的问题。为了解决这个问题。python引入了标记清除,和分代回收。在内部建了四个链表

    1. refchain (python自己的环状双向链表)
    2. 2代:第一代扫描十次
    3. 1代:第零代扫描十次
    4. 0代:有700个数据

    当源码内部达到各自的阈值后,就会触发扫描标记清除动作。

    源码内部上述提出了优化机制(缓存机制)

  • 缓存机制

    1. 池(int)

      目的:为了避免重复创建和销毁对象、维护chi

      # 启动解释器时 Python内部会自动帮我们创建-4~256
      v1 = 7  # 不会创建一个对象,会直接从池中进行获取
      v2 = 9  # 不会创建一个对象,会直接从池中进行获取
      v3 = 9  # 不会创建一个对象,会直接从池中进行获取
      # 此时 v2和v3的id地址是一样的
      
      v4 = 5555   # 会创建一个对象
      v5 = 666	# 会创建一个对象
      v6 = 666	# 会创建一个对象
      # 此时v5和v6的地址是不一样的
      
    2. free_list(tuple、list、float、dict)

      a = 3.14  # 创建一个Float的对象
      
      del a  # 不会进行销毁,经对象添加到free_list当中,当free_list满了,才会进行销毁
      
      a = 34.434  # 当第二次进行创建对象的时候不会直接创建对象,而是从free_list中获取到a,然后进行初始化,然后在放到refchain当中
      
      a = (1,3,4)
      """
      元组中的free_list
      free_list = [0,1,2,3,4,5,6,7,8........19]
      元组中的元素个数为0是,放在free_list中的0号里面
      元组中的元素为个数1时,放在free_list中的1号里面
      ......
      元组中的元素个数为19,放在free_list中的19号里面
      """
      tuple1 = (1,2)
      del tuple
      tuple2 = (3,4)  # 不会直接销毁元组,而是把他放在free_list的2号里面,然后进行初始化值
      

      维护unicode_latin1[256]链表,内部将所有的ascii字符缓存起来,以后使用时就不再反复创建。

      str1 = 'niaho'
      del a 
      str2 = 'niaho'
      # 此时str1和str2的内存地址是一样的
      # 除此之外,Python内部还对字符串做了驻留机制,针对那么只含有字母、数字、下划线的字符串(见源码Objects/codeobject.c),如果内存中已存在则不会重新在创建而是使用原来的地址里(不会像free_list那样一直在内存存活,只有内存中有才能被重复利用)。
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值