zoukankan      html  css  js  c++  java
  • python扩展—垃圾回收和驻留机制

    一.垃圾回收机制

    垃圾回收机制是自动帮助我们管理内存,清理垃圾的一种工具

    一.堆区与栈区
    ​在定义变量时,变量名与变量值都是需要存储的,分别对应内存中的两块区域:堆区与栈区。
    1、变量名与值内存地址的关联关系存放于栈区
    2、变量值存放于堆区,内存管理回收的则是堆区的内容,

    二.直接引用与间接引用
    ​1.直接引用指的是从栈区出发直接引用到的内存地址。
    ​2.间接引用指的是从栈区出发引用到堆区后,再通过进一步引用才能到达的内存地址。

    1.引用计数

    当一个对象的引用被创建或者复制时,对象的引用计数加1;
    当一个对象的引用被销毁时,对象的引用计数减1;
    当对象的引用计数减少为0时,就意味着对象已经没有被任何人使用了,可以将其所占用的内存释放了。
    
    优点:
    简单、直观
    实时性,只要没有了引用就释放资源。
    缺点:
    维护引用计数需要消耗一定的资源
    循环应用时,无法回收。也正是因为这个原因,才需要通过标记-清理和分代收集机制来辅助引用计数机制。
    

    变量值被关联次数的增加或减少,都会引发引用计数机制的执行(增加或减少值的引用计数),这存在明显的效率问题。
    如果说执行效率还仅仅是引用计数机制的一个软肋的话,那么很不幸,引用计数机制还存在着一个致命的弱点,即循环引用(也称交叉引用)

    如下我们定义了两个列表,简称列表1与列表2,变量名l1指向列表1,变量名l2指向列表2
    >>> l1=['xxx']  # 列表1被引用一次,列表1的引用计数变为1   
    >>> l2=['yyy']  # 列表2被引用一次,列表2的引用计数变为1   
    >>> l1.append(l2)             # 把列表2追加到l1中作为第二个元素,列表2的引用计数变为2
    >>> l2.append(l1)             # 把列表1追加到l2中作为第二个元素,列表1的引用计数变为2
    
    l1与l2之间有相互引用
    l1 = ['xxx'的内存地址,列表2的内存地址]
    l2 = ['yyy'的内存地址,列表1的内存地址]
    >>> l1
    ['xxx', ['yyy', [...]]]
    >>> l2
    ['yyy', ['xxx', [...]]]
    >>> l1[1][1][0]
    'xxx'
    

    循环引用会导致:值不再被任何名字关联,但是值的引用计数并不会为0,应该被回收但不能被回收,什么意思呢?试想一下,请看如下操作

    >>> del l1 # 列表1的引用计数减1,列表1的引用计数变为1
    >>> del l2 # 列表2的引用计数减1,列表2的引用计数变为1
    

    此时,只剩下列表1与列表2之间的相互引用,两个列表的引用计数均不为0,但两个列表不再被任何其他对象关联,没有任何人可以再引用到它们,所以它俩占用内存空间应该被回收,但由于相互引用的存在,每一个对象的引用计数都不为0,因此这些对象所占用的内存永远不会被释放,所以循环引用是致命的,这与手动进行内存管理所产生的内存泄露毫无区别。 所以Python引入了“标记-清除” 与“分代回收”来分别解决引用计数的循环引用与效率低的问题

    2.标记-清除(用来解决循环引用带来的内存泄露问题)

    “标记-清除”不改动真实的引用计数,而是将
    集合中对象的引用计数复制一份副本,改动该对象引用的副本。对于副
    本做任何的改动,都不会影响到对象生命走起的维护。
    

    3.分代回收(用来降低引用计数的扫描频率,提升垃圾回收的效率)

    将系统中的所有内存块根据其存活时间划分为不同的集合,
    每一个集合就成为一个“代”,垃圾收集的频率随着“代”的存活时间的增大而减小。
    也就是说,活得越长的对象,就越不可能是垃圾,就应该减少对它的垃圾收集频率。
    那么如何来衡量这个存活时间:通常是利用几次垃圾收集动作来衡量,
    如果一个对象经过的垃圾收集次数越多,可以得出:该对象存活时间就越长。
    

    总结

    1、标记
    通俗地讲就是:
    栈区相当于“根”,凡是从根出发可以访达(直接或间接引用)的,都称之为“有根之人”,有根之人当活,无根之人当死。
    具体地:标记的过程其实就是,遍历所有的GC Roots对象(栈区中的所有内容或者线程都可以作为GC Roots对象),然后将所有GC Roots的对象可以直接或间接访问到的对象标记为存活的对象,其余的均为非存活对象,应该被清除。
    2、清除
    清除的过程将遍历堆中所有的对象,将没有标记的对象全部清除掉。
    

    二.驻留机制

    对于短字符串,将其赋值给多个不同的对象时,内存中只有一个副本,多个对象共享该副 
    本。长字符串不遵守驻留机制。
    
    驻留适用范围: 由数字,字符和下划线(_)组成的python标识符以及整数[-5,256]。 
    
  • 相关阅读:
    开门大吉效果
    Python3-sqlalchemy-orm 创建关联表带外键并插入数据
    Python3-sqlalchemy-orm 联表查询-无外键关系
    Python3-sqlalchemy-orm 分组统计
    Python3-sqlalchemy-orm 回滚
    Python3-sqlalchemy-orm 查询、修改
    Python3-sqlalchemy-orm
    MySQL 5.6的一个bug引发的故障
    MySQL GTID你知多少
    MySQL Cluster 日常维护
  • 原文地址:https://www.cnblogs.com/chenwenyin/p/12344360.html
Copyright © 2011-2022 走看看