zoukankan      html  css  js  c++  java
  • Python:对象存储&拷贝&垃圾回收机制

    一、Python的对象存储有2种方式:

    1)小整数池:-5到256的数字都存在这里,当我们定义一个对象时,不会重新分配内存地址,而是指向到小整数池的一个位置;

    栗子:

    a=25

    b=25

    c=288

    d=288

    其中:a 和 b 的内存地址是一个;c 和 d 指向2个内存地址;

    2)intern机制(大整数池):用来存放一些字符串(数字、字母、下划线的组合),如果包含特殊字符,则不在大整数池中;

    栗子:

    str1='abc_001'

    str2='abc_001'

    str3='abc+001'

    str4='abc+001'

    其中:str1 和 str2 的内存地址是一个;str3 和 str4 指向2个内存地址(包含特殊字符+);

    二、浅拷贝和深拷贝

    举个栗子:

    import copy 

    name=['lili','lucy']

    list1=['lilei',name]                 #  list1=['lilei','lili','lucy']

    list2=list1                             #  指向list1  list2=['lilei','lili','lucy']

    list3=list1.copy()                  #  浅拷贝     list3=['lilei',name]

    list4=copy.deepcopy(list1)  #   深拷贝      list4=['lilei',X]  X=['lili','lucy']

    其中:list2是指向list1,和list1的内存地址一样。list3 和 list4是对list1的拷贝,内存地址不同。

    对name列表进行改变,name.append('pete'),然后查看几个列表的值:

    list1=['lilei','lili','lucy','pete']

    list2=['lilei','lili','lucy','pete']

    list3=['lilei','lili','lucy','pete' #浅拷贝是拷贝的列表名称,所以在对应列表发生变化后,此处会变化

    list4=['lilei','lili','lucy']           #由于深拷贝把里面的列表值也拷贝过来了,所以在对应列表发生变化后,此处不会变化

    三、内存回收机制

    Python的垃圾回收,采取的是引用计数为主,标记-清除+分代回收为辅的回收策略。

    1)引用计数机制:

    对于每一个对象的使用标记次数。

    如a=1000,此时1000被引用1次,当把a重新赋值时,1000的引用次数变成了0,此时就可以进行回收了。

    如果a=1000,b=a,那边1000就被引用了2次。

    对于小整数池和大整数池的值,再不被对象引用时,不存在回收的说法。

    如果2个对象存在相互引用的情况,如:a=[1,2] b=[2,3]  a.append(b[1])  b.append(a[0]) ,在这种情况下如果还采用引用计数的方式进行回收就会陷入死循环,导致内存泄漏。

    2)标记-清除机制:

    标记清除算法是一种基于追踪回收技术实现的垃圾回收算法。

    它分为2个阶段:第一阶段是标记阶段,GC会吧所有活动的对象打上标记,第二阶段就是把那些没有被标记的对象进行回收。

    对象之间通过引用(指针)进行连接,构成一个有向图。从根对象出发,沿着有向边进行遍历对象,可达的对象标记为活动对象,不可达的对象就是要被清除的非活动对象。根对象就是全局变量、调用栈等。

    如图中,根对象指向1,1再指向 2 和 3 ,3又重复指向了2 。 4 和 5 没有被指向,所以会被回收。

    标记清除算法作为Python的辅助垃圾收集技术,主要处理的是一些容器对象,比如list、dict、tuple等,

    因为对于字符串、数值对象是不可能造成循环引用问题。

    Python使用一个双向链表将这些容器对象组织起来。

    不过,这种简单粗暴的标记清除算法也有明显的缺点:清除非活动的对象前它必须顺序扫描整个堆内存,哪怕只剩下小部分活动对象也要扫描所有对象。 

    3)分代回收机制

    分代回收策略着眼于提升垃圾的回收效率。

    在任何的编程中,对于变量在内存中的创建/销毁,总有频繁和不那么频繁。比如任何程序中总有全局变量和部分变量,而在垃圾回收前,需要先进行一次垃圾检测,即检测这个对象是不是垃圾,该不该被回收。

    当对象很多的时候,垃圾检测将耗费比垃圾回收更多的时间。对于这种多对象的程序,我们可以把一些频率相同的对象称为“同一代”对象,垃圾检测的时候可以对频率高的多检测基础,反之可以少检测几次。这样就提高了垃圾回收的效率。

    Python中设有一代回收列表、二代回收列表和三代回收列表。

    当一代回收列表中的值超过700个时,就会将引用计数次数为0的值进行回收,如此回收10次后,就会把第一代列表中的数据存入第二代回收列表中,如此再到第三代回收列表中。

    对于循环银行的情况,一般的垃圾回收方式肯定是无效的了,这个时候就需要显示地调用一些操作来保证垃圾的回收 和内存不泄露。这就要用到Python内建的垃圾回收gc模块了。

     

  • 相关阅读:
    二叉搜索树
    【树】List Leaves
    模板——dijkstra单源最短路
    余数求和——除法分块
    倍增——ST表
    线段树——内存池
    线段树——模板
    洛谷 P1498 南蛮图腾
    洛谷 P2199 最后的迷宫
    洛谷 P1495 中国剩余定理
  • 原文地址:https://www.cnblogs.com/test123/p/14211973.html
Copyright © 2011-2022 走看看