内存管理机制

什么是内存管理机制

  1. python中创建的对象的时候,首先会去申请内存地址,然后对对象进行初始化,所有对象都会维护在一
    个叫做refchain的双向循环链表中,每个数据都保存如下信息:

     1. 链表中数据前后数据的指针
     2. 数据的类型
     3. 数据值
     4. 数据的引用计数
     5. 数据的长度(list,dict..)
    

内存管理机制包括:引用计数机制,数据池和缓存,标记清除,分代回收

一、引用计数机制

  1. 引用计数增加:
    1.1 对象被创建
    1.2 对象被别的变量引用(另外起了个名字)
    1.3 对象被作为元素,放在容器中(比如被当作元素放在列表中)
    1.4 对象被当成参数传递到函数中

    import sys
    
    a = [11,22]        # 对象被创建
    b = a             # 对象被别的变量引用
    c = [111,222,333,a]        # 对象被作为元素,放在容器中
    
    # 获取对象的引用计数
    print(sys.getrefcount(a))     # 对象被当成参数传递到函数中
    

    最后的执行结果是,a 这个变量被引用了4次

    在这里插入图片描述

  2. 引用计数减少:
    2.1 对象的别名被显式的销毁
    2.2 对象的一个别名被赋值给其他对象 (例:比如原来的a=10,被改成a=100,此时10的引用计数就减少了)
    2.3 对象从容器中被移除,或者容器被销毁(例:对象从列表中被移除,或者列表被销毁)
    2.4 一个引用离开了它的作用域(调用函数的时候传进去的参数,在函数运行结束后,该参数的引用即被销毁)

    import sys
    
    del b      # 对象的别名被显式的销毁
    b = 999        # 对象的一个别名被赋值给其他对象
    del c      # 列表被销毁(容器被销毁)
    c.pop()    # 把列表数据最后一个删除掉(对象从容器中被移除)
    

二、数据池和缓存

数据池分为两种:小整数池 和 大整数池

  1. 小整数池(-5到256之间的数据)

    运行机制:Python自动将 -5~256 的整数进行了缓存到一个小整数池中,当你将这些整数赋值给变量时,并不会重新
    创建对象,而是使用已经创建好的缓存对象,当删除这些数据的引用时,也不会进行回收

    超出-5到256的整数将不会在在缓存,会重新创建对象

    例如:

    对于超出-5到256的整数将不会在在缓存,Python会重新创建对象,返回id

    # 场景1:数据为列表,不在-5~256 的范围
    >>> a = [11]
    >>> b = [11]
    >>> id(a),id(b)
    (1693226918600, 1693231858248)       ========id 不一样
    
    # 场景二: 数据为整数,在-5~256 的范围
    >>> aa = 11
    >>> bb = 11
    >>> id(aa),id(bb)
    (140720470385616, 140720470385616)    id 一样
    
    # 场景三: 数据不在-5~256的范围
    >>> bb = -7
    >>> aa = -7
    >>> id(aa),id(bb)
    (1843518717904, 1843518717776)      id 不一样
    
    # 场景四: 数据不在-5~256的范围
    >>> a = 257
    >>> b = 257
    >>> id(a),id(b)
    (2092420910928, 2092420911056)      id 不一样
    
  2. 大整数池(字符串驻留池 / intern机制)

    优点:在创建新的字符串对象时,会先在缓存池里面找是否有已经存在的值相同的对象(标识符,即只包含数字、字母、下划线的字符串),如果有,则直接拿过来用(引用),避免频繁的创建和销毁内存,提升效率

    例如:

    对于不在标识符内的数据将不会在在缓存,Python会重新创建对象,返回id

    # 场景1:
    >>> a = '123adsf_'
    >>> b = '123adsf_'
    >>> id(a),id(b)
    (61173296, 61173296)        ========id 一样
    
    # 场景二: 
    >>> b1 = '123adsf_?'
    >>> b2 = '123adsf_?'
    >>> id(b1),id(b2)
    (61173376, 61173416)        id 不一样
    
  3. 缓存机制

    在这里插入图片描述
    对于python中常用内置数据类型的缓存:

    float:缓存100个对象
    list: 80个对象
    dict: 80个对象
    set: 80个对象
    元组:会根据元组数据的长度,分别缓存元组长度为0-20的对象

三、标记清除

python中的垃圾回收:主要是以引用计数为主,标记清除和分代回收为辅助策略

 - 引用计数:当对象的引用计数为0时,对象会被销毁,释放内存
 - 标记清除:解决引用计数出现的循环引用的问题
 - 分代回收:控制垃圾回收触发的频率
  1. 引用计数存在一个缺点:
    那就是当两个对象出现循环引用的时候,那么这个两个变量始终不会被销毁,这样就会导致内存泄漏
    在这里插入图片描述

  2. 标记清除的作用:
    解决引用计数出现的循环引用的问题

四、分代回收

  1. 分代回收(700, 10, 10)的概念:控制垃圾回收触发的频率

  2. 如果获取分代回收的频率 :

    python中有个GC模块:

    1. gc.get_threshold:获取分代回收的频率
      返回的是(700,10,10),这也是gc的默认值。
    2. gc.set_threshold: 设置分代回收的频率
  3. 分代回收的频率(700,10,10)是什么意思?
    整个分代回收分成三代来进行回收:

    1. 第0代链表:
      当我们创建的对象在程序里达到700个后,会触发一轮垃圾回收,将这个700个对象全部遍历出来,
      挨个进行扫描,看哪个对象引用计数是为0的,为0的直接被回收掉,进行释放内存
    2. 第一代链表:
      而不为0的数据,则放进去第一代里面,当第0代链表回收了10次之后,则第一代链表会触发一轮垃圾回收,
      将第一代的对象全进行扫描,有引用计数是为0的,直接被回收掉
    3. 第二代链表:
      引用计数不为0的,放到下一代,则放进去第二代链表里面,当第一代链表回收了10次之后,则第二代链表会触发
      一轮垃圾回收(相当于0代列表触发100,二代才会触发一次),未被回收的不做处理

    在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值