专栏名称: Python之禅
分享Python相关技术干货,偶尔扯扯其它的
目录
相关文章推荐
Python爱好者社区  ·  国企官网被挂上“码农的钱你也敢吞,还钱” ·  昨天  
Python中文社区  ·  揭秘 DeepSeek ... ·  昨天  
Python开发者  ·  DeepSeek 下棋靠忽悠赢了 ... ·  3 天前  
Python爱好者社区  ·  “给我滚出贵大!”郑强出任贵州大学校长,打算 ... ·  4 天前  
Python爱好者社区  ·  节后第一个私活,赚了3w ·  3 天前  
51好读  ›  专栏  ›  Python之禅

Python中的“垃圾”是怎么回收的?

Python之禅  · 公众号  · Python  · 2019-12-13 11:54

正文

作者:heroyf     链接:
https://www.heroyf.club/2019/10/23/python_gc/

前言

对于python来说,一切皆为对象,所有的变量赋值都遵循着对象引用机制。程序在运行的时候,需要在内存中开辟出一块空间,用于存放运行时产生的临时变量;计算完成后,再将结果输出到永久性存储器中。如果数据量过大,内存空间管理不善就很容易出现 OOM(out of memory),俗称爆内存,程序可能被操作系统中止。

而对于服务器,内存管理则显得更为重要,不然很容易引发内存泄漏- 这里的泄漏,并不是说你的内存出现了信息安全问题,被恶意程序利用了,而是指程序本身没有设计好,导致程序未能释放已不再使用的内存。- 内存泄漏也不是指你的内存在物理上消失了,而是意味着代码在分配了某段内存后,因为设计错误, 失去了对这段内存的控制 ,从而造成了内存的浪费。也就是这块内存脱离了gc的控制

计数引用

因为python中一切皆为对象,你所看到的一切变量,本质上都是对象的一个指针。

当一个对象不再调用的时候,也就是当这个对象的引用计数(指针数)为 0 的时候,说明这个对象永不可达,自然它也就成为了垃圾,需要被回收。可以简单的理解为没有任何变量再指向它。

  1. import os

  2. import psutil


  3. # 显示当前 python 程序占用的内存大小

  4. def show_memory_info(hint):

  5. pid = os.getpid()

  6. p = psutil.Process(pid)


  7. info = p.memory_full_info()

  8. memory = info.uss / 1024. / 1024

  9. print('{} memory used: {} MB'.format(hint, memory))



  10. def func():

  11. show_memory_info('initial')

  12. a = [i for i in range(10000000)]

  13. show_memory_info('after a created')


  14. func()

  15. show_memory_info('finished')


  16. ########## 输出 ##########

  17. initial memory used: 47.19140625 MB

  18. after a created memory used: 433.91015625 MB

  19. finished memory used: 48.109375 MB

可以看到调用函数 func(),在列表 a 被创建之后,内存占用迅速增加到了 433 MB:而在函数调用结束后,内存则返回正常。这是因为,函数内部声明的列表 a 是局部变量,在函数返回后,局部变量的引用会注销掉;此时,列表 a 所指代对象的引用数为 0,Python 便会执行垃圾回收,因此之前占用的大量内存就又回来了。

  1. def func():

  2. show_memory_info('initial')

  3. global a

  4. a = [i for i in range(10000000)]

  5. show_memory_info('after a created')


  6. func()

  7. show_memory_info('finished')


  8. ########## 输出 ##########


  9. initial memory used: 48.88671875 MB

  10. after a created memory used: 433.94921875 MB

  11. finished memory used : 433.94921875 MB

新的这段代码中,global a 表示将 a 声明为全局变量。那么,即使函数返回后,列表的引用依然存在,于是对象就不会被垃圾回收掉,依然占用大量内存。同样,如果我们把生成的列表返回,然后在主程序中接收,那么引用依然存在,垃圾回收就不会被触发,大量内存仍然被占用着:

  1. def func():

  2. show_memory_info('initial')

  3. a = [i for i in derange(10000000)]

  4. show_memory_info('after a created')

  5. return a


  6. a = func()

  7. show_memory_info('finished')


  8. ########## 输出 ##########


  9. initial memory used: 47.96484375 MB

  10. after a created memory used: 434.515625 MB

  11. finished memory used: 434.515625 MB

那怎么可以看到变量被引用了多少次呢?通过 sys . getrefcount

  1. import sys


  2. a = []


  3. # 两次引用,一次来自 a,一次来自 getrefcount

  4. print( sys.getrefcount(a))


  5. def func(a):

  6. # 四次引用,a,python 的函数调用栈,函数参数,和 getrefcount

  7. print(sys.getrefcount(a))


  8. func(a)


  9. # 两次引用,一次来自 a,一次来自 getrefcount,函数 func 调用已经不存在

  10. print(sys.getrefcount(a))


  11. ########## 输出 ##########


  12. 2

  13. 4

  14. 2

如果其中涉及函数调用,会额外增加两次1. 函数栈2. 函数调用

从这里就可以看到python不再需要像C那种的认为的释放内存,但是python同样给我们提供了手动释放内存的方法 gc . collect ()

  1. import gc


  2. show_memory_info('initial')


  3. a = [i for i in range(10000000)]


  4. show_memory_info('after a created')


  5. del a

  6. gc .collect()


  7. show_memory_info('finish')

  8. print(a)


  9. ########## 输出 ##########


  10. initial memory used: 48.1015625 MB

  11. after a created memory used: 434.3828125 MB

  12. finish memory used: 48.33203125 MB


  13. ---------------------------------------------------------------------------

  14. NameError Traceback (most recent call last)

  15. <ipython-input-12-153e15063d8a> in

  16. 11

  17. 12 show_memory_info('finish')

  18. ---> 13 print(a)


  19. NameError: name 'a' is not defined

截止目前,貌似python的垃圾回收机制非常的简单,只要对象引用次数为0,必定为触发gc,那么引用次数为0是否是触发gc的充要条件呢?

循环回收

如果有两个对象,它们互相引用,并且不再被别的对象所引用,那么它们应该被垃圾回收吗?

  1. def func():

  2. show_memory_info('initial')

  3. a = [i for i in range(10000000)]

  4. b = [i for i in range(10000000)]

  5. show_memory_info('after a, b created')

  6. a.append(b)

  7. b.append(a)


  8. func()

  9. show_memory_info('finished')


  10. ########## 输出 ##########


  11. initial memory used:







请到「今天看啥」查看全文