zoukankan      html  css  js  c++  java
  • Python:垃圾回收

    有很多不同的方法来实现垃圾回收,例如跟踪,引用计数,转义分析,时间戳和心跳信号等。不同的语言依赖于不同的垃圾回收实现,例如,有些将其与编译器和运行时系统集成在一起。而其他语言则可能需要事后设置,甚至可能需要重新编译。Python中垃圾收集器使用基于引用计数的方法。它在程序执行期间运行,并在对象的引用计数达到0时开始工作。


    1、引用管理

    首先,内存管理是基于引用的管理。我们知道Python中,引用与对象是分离的,一个对象可以有多个引用,而每个对象都存有指向自己的引用计数。可以使用标准库sys查看某个对象的引用计数:

    from sys import getrefcount
    
    a = [1,2,3]
    print(getrefcount(a)) # 打印2
    
    b = a
    print(getrefcount(a)) # 打印3
    

    由于调用getrefcount()时又创建了一次引用,所以打印的引用计数会比实际多一个。


    2、对象引用对象

    Python中对象会引用别的对象,而容器对象的引用会构成很复杂的拓扑结构:

    l = [1,2,3]
    d = {"k": l}
    
    y = [l, d]
    z = [y,(l,y)]
    

    使用 objgraph 包可以绘制引用关系:

    ...
    import objgraph
    objgraph.show_refs([z], filename='sample-graph.png')
    

    绘制的 z 对象的引用图如下:


    3、引用环

    两个对象相互引用,即构成了所谓的引用环

    a = []
    b = [a]
    a.append(b)
    
    objgraph.show_refs([a,b], filename='a-b.png')
    

    即使是单个对象,只需自己引用自己,也会构成引用环:

    a = []
    a.append(a)
    
    objgraph.show_refs([a], filename='a-b.png')
    

    del关键字除了可以删除容器中的元素,还可以删除某个引用。


    4、垃圾回收

    CPython中的内存管理和垃圾回收有两个策略:

    • 引用计数

    • 分代回收


    4.1 引用计数

    CPython中主要的垃圾收集机制是通过引用计数,且引用计数无法被禁用,而后面谈到的分代回收策略则可以禁止。

    原理上,Python的某个对象的引用计数变为0时,就要成为被回收的垃圾了。例如:

    a = [1,2,3]
    del a
    

    当垃圾回收启动时,Python扫描到这个引用计数为0的对象,会将其所占据的内存清空。而垃圾回收是个费时费力的事,垃圾回收期间Python不能进行其他任务。频繁的垃圾回收会大大降低Python的效率,所以Python只会在特定条件下启动垃圾回收。Python运行时,会记录其中分配对象和取消分配对象的次数,当两者差值高于某个阈值,垃圾回收才会启动。


    4.2 分代回收

    除了上面这种实时的自动的基于引用计数的垃圾回收实现方法,Python还同时采用分代回收策略,这一次略的基本假设是,存活时间越久的对象,越不可能在以后成为垃圾。Python将所有对象分为三代,所有新建对象都是0代,如果经过一次扫描没被回收即成为了1代,以此类推。


    Python的基于引用计数的方法是自动的,并且是实时发生的,而分代垃圾回收模块的操作是周期性的,可以手动调用,常用API:
    • get_shreshold()方法可以查看触发垃圾收集的阈值:

    • gc.get_count()方法可以查看内存中当前存在的各代对象数量

    • gc.set_threshold()方法可以更改触发垃圾收集的阈值

    >>> import gc
    >>> print(gc.get_threshold())    # (700, 10, 10)
    >>> gc.set_threshold(700.10,5) # 2代垃圾回收会更频繁
    >>> gc.collect() # 手动触发垃圾回收
    

    对于每一代,垃圾收集器模块都有一个阈值对象。如果对象数超过该阈值,则垃圾收集器将触发收集过程,在该过程中幸存下来的对象会被归为下一代。默认情况下,Python对于最年轻的一代的阈值为700,对于两个较老的一代中的每个阈值为10。


    引用环的回收

    分代回收可以检测和解决引用环问题,在Python 1.5中引入了循环检测算法,它跟踪容器对象,因为只有它们才能创建这种引用环。

    循环检测算法的基本原理是:Python会复制每个对象的引用计数,记为gc_ref。假设每个对象为i,该对象的计数为gc_ref_i。Python会遍历所有的对象i,对于每个对象i所引用的对象j,将jgc_ref_j减1:

    遍历后,gc_ref不为0的对象及这些对象引用的对象,以及更下游的对象会被保留,而引用环中的对象会被回收。


    参考

  • 相关阅读:
    使用textarea标签按Enter键后web页面中成换行 vue
    关于json数组对象和对象数组遇到的一些问题
    vue.js实现checkbox的全选和反选
    关于arcgis js 中要素更新的问题
    C# 图片上传问题
    arcgis js 几种拓扑关系详解
    ISS部署网站--HTTP 错误 404.17
    ADODB.Stream在进行文件上传时报错
    window.open 打开Excel或者Word 无权限问题
    Aspose.cell生成表格
  • 原文地址:https://www.cnblogs.com/bqzzz/p/14530688.html
Copyright © 2011-2022 走看看