Python 的内存是如何管理的 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
CicadaMan
V2EX    Python

Python 的内存是如何管理的

  •  
  •   CicadaMan 2020-03-13 20:48:47 +08:00 2356 次点击
    这是一个创建于 2104 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如果写过 C 和 C++的小伙伴肯定都知道,程序中的内存管理是非常关键的,一不小心可能就会产生内存泄漏。但是我们在写 Python 的时候好像从来没有关心过内存的处理,为什么可以这么爽?在你爽的背后,实际上是 Python 在默默的帮你管理着,具体怎么实现的,听我慢慢道来。

    一、引用计数:

    在 Python 中,使用了引用计数这一技术实现内存管理。一个对象被创建完成后就有一个变量指向他,那么就说明他的引用计数为 1,以后如果有其他变量指向他,引用计数也会相应增加,如果将一个变量不再执行这个对象,那么这个对象的引用计数减 1。如果一个对象没有任何变量指向他,也即引用计数为 0,那么这个对象会被 Python 回收。示例代码如下:

    class Person(object): def __init__(self,name): self.name = name def __del__(self): print('%s 执行了 del 函数'%self.name) while True: p1 = Person('p1') p2 = Person('p2') del p1 del p2 a = input('test:') 

    可以看到,两个对象的 del 函数都得到了执行。(欢迎加公众号:pythonjs,持续分享干货)

    二、循环引用:

    引用计数这一技术虽然可以在一定程度上解决内存管理的问题。但是还是有不能解决的问题,即循环引用。比如现在有两个对象分别为 a 和 b,a 指向了 b,b 又指向了 a,那么他们两的引用计数永远都不会为 0。也即永远得不到回收。看以下示例:

    class Person(object): def __init__(self,name): self.name = name def __del__(self): print('%s 执行了 del 函数'%self.name) while True: p1 = Person('p1') p2 = Person('p2') # 循环引用后,永远得不到释放 p1.next = p2 p2.prev = p1 del p1 del p2 a = input('test:') 

    可以看到,del 函数是不会运行的,是因为循环引用导致两个对象得不到释放。(欢迎加公众号:pythonjs,持续分享干货)

    三、标记清除和分代回收:

    在 Python 程序中,每次你新创建了一个对象,那么就会将这个对象挂到一个叫做零代链表中(当然这个链表是 Python 内部的,Python 开发者是没法访问到的)。比如现在你在程序中创建四个 Person 对象,分别叫做 p1、p2、p3 以及 p4,然后 p1 与 p2 之间互相引用,并且让 p3 和 p4 的引用计数为 2,示例代码如下:

    import sys class Person(object): def __init__(self,name): self.name = name self.next = None self.prev = None p1 = Person('p1') p2 = Person('p2') p3 = Person('p3') p4 = Person('p4') p1.next = p2 p2.prev = p1 temp1 = p3 temp2 = p4 print(sys.getrefcount(p1)) print(sys.getrefcount(p2)) print(sys.getrefcount(p3)) print(sys.getrefcount(p4)) 

    以上代码实际上就会将 p1 和 p2 以及 p3 和 p4 挂在一个叫做零代链表中,示例图如下:

    我们可以看到,这时候 p1 引用了 p2,而 p2 又引用了 p1,因此这两个对象产生了循环引用。在后期即使我删除了 del p1 以及 del p2,那么这两个对象也会得不到释放。

    因此这时候 Python 就启用了一个新的垃圾回收的机制。如果创建的对象总和减去被释放的对象,达到一定的值(某个阈值),那么 Python 就会遍历这个零代链表,找到那些有相互引用的对象,将这些对象的引用计数减 1,如果引用计数值为 0 了,那么就说明这个对象是可以被释放的,比如以上 p1 和 p2,这时候就会释放 p1 和 p2。接下来再将没有被释放的对象,挪动到一个新的链表中,这个链表叫做一代链表

    在零代链表清理的次数达到某个阈值后,Python 会去遍历一代链表,将那些没有得到释放的对象移动到二代链表。同样的原理,如果一代链表清理的次数达到某个阈值后,Python 会去遍历二代链表,把垃圾对象进行回收。(欢迎加公众号:pythonjs,持续分享干货)

    四、弱代假说:

    来看看代垃圾回收算法的核心行为:垃圾回收器会更频繁的处理新对象。一个新的对象即是你的程序刚刚创建的,而一个来的对象则是经过了几个时间周期之后仍然存在的对象。Python 会在当一个对象从零代移动到一代,或是从一代移动到二代的过程中提升(promote)这个对象。

    为什么要这么做?这种算法的根源来自于弱代假说(weak generational hypothesis)。这个假说由两个观点构成:首先是年轻的对象通常死得也快,而老对象则很有可能存活更长的时间。

    假定现在我用 Python 创建一个新对象:

    根据假说,我的代码很可能仅仅会使用 ABC 很短的时间。这个对象也许仅仅只是一个方法中的中间结果,并且随着方法的返回这个对象就将变成垃圾了。大部分的新对象都是如此般地很快变成垃圾。然而,偶尔程序会创建一些很重要的,存活时间比较长的对象-例如 web 应用中的 session 变量或是配置项。

    通过频繁的处理零代链表中的新对象,Python 的垃圾收集器将把时间花在更有意义的地方:它处理那些很快就可能变成垃圾的新对象。同时只在很少的时候,当满足阈值的条件,收集器才回去处理那些老变量。

    另外我们会不断分享技术在公众号:pythonjs,您如果不嫌弃,可以关注一下哦

    lithbitren
        1
    lithbitren  
       2020-03-13 23:08:22 +08:00
    资瓷,解决了我的一个小疑惑。
    lithbitren
        2
    lithbitren  
       2020-03-13 23:09:08 +08:00
    顺道问一下,代码是这么插入的啊,妹找到教程啊
    CEBBCAT
        3
    CEBBCAT  
       2020-03-13 23:53:06 +08:00 via Android
    @lithbitren V2 支持 Markdown 语法呀。本站应该有使用指南节点
    lithbitren
        4
    lithbitren  
       2020-03-14 00:09:45 +08:00
    @CEBBCAT 没找到指南啊,反引号`我试过了好像没啥用啊,在网上查到的其他语法好像也没有解析昂,还是说只在主楼显示,回复无法显示。
    CEBBCAT
        5
    CEBBCAT  
       2020-03-14 00:16:35 +08:00 via Android   1
    lithbitren
        6
    lithbitren  
       2020-03-14 00:20:58 +08:00
    @CEBBCAT 蟹蟹,原来是回复里不支持 Markdown 语法
    lithbitren
        7
    lithbitren  
       2020-03-14 00:22:40 +08:00
    @CEBBCAT 主楼的帖子,关于垃圾回收的变量链表,在我另一个帖子里发现,当达到阈值时触发垃圾回收时,并不会清除掉所有不可到达的变量,每次都会残留几个,这是啥原因昂。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     4144 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 35ms UTC 10:11 PVG 18:11 LAX 02:11 JFK 05:11
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86