Python3.7学习笔记30-垃圾回收机制

一、前言

Python 程序在运行的时候,需要在内存中开辟出一块空间,用于存放运行时产生的临时变量;计算完成后,再将结果输出到永久性存储器中。如果数据量过大,内存空间管理不善就很容易出现 OOM(out of memory),俗称爆内存,程序可能被操作系统中止。而对于服务器,这种设计为永不中断的系统来说,内存管理则显得更为重要,不然很容易引发内存泄漏,导致程序未能释放已不再使用的内存。这种问题意味着代码在分配了某段内存后,因为设计错误,失去了对这段内存的控制,从而造成了内存的浪费。在python中。一般来说。当某个对象不在被引用时候。它会自动执行垃圾回收。

二、垃圾回收

import os
import psutil# 显示当前 python 程序占用的内存大小
def show_memory_info(hint):pid = os.getpid()p = psutil.Process(pid)info = p.memory_full_info()memory = info.uss / 1024. / 1024print('{} ,占用内存: {} MB'.format(hint, memory))def func():show_memory_info('变量创建前')a = [i for i in range(10000000)]show_memory_info('变量创建后')func()
show_memory_info('执行完函数之后')/usr/local/bin/python3.7 /Users/zhonglinglong/PycharmProjects/whole_world/临时文件.py
变量创建前 ,占用内存: 47.90625 MB
变量创建后 ,占用内存: 437.4140625 MB
执行完函数之后 ,占用内存: 50.88671875 MBProcess finished with exit code 0
  • 可以看到。创建变量之后内存占用增加接近400MB。当执行完成函数之后。内存释放来接近400MB。说明在a不在使用之后。所占用的内存已经被执行垃圾回收然后释放资源

我们把代码修改一个地方。创建变量之前定义成全局变量。可以看到。在执行完函数之后。内存并没有释放出来。同样的如果把函数的返回结果设置成变量。然后给统计的函数接受这个变量。内存也不会释放。这就是我们说的。当对象被引用的时候。不会自动回收。

import os
import psutil# 显示当前 python 程序占用的内存大小
def show_memory_info(hint):pid = os.getpid()p = psutil.Process(pid)info = p.memory_full_info()memory = info.uss / 1024. / 1024print('{} ,占用内存: {} MB'.format(hint, memory))def func():show_memory_info('变量创建前')global aa = [i for i in range(10000000)]show_memory_info('变量创建后')func()
show_memory_info('执行完函数之后')/usr/local/bin/python3.7 /Users/zhonglinglong/PycharmProjects/whole_world/临时文件.py
变量创建前 ,占用内存: 47.91796875 MB
变量创建后 ,占用内存: 437.421875 MB
执行完函数之后 ,占用内存: 437.421875 MBProcess finished with exit code 0

三、对象的引用

我们知道对象如果没有被引用所占用的内存会被回收。资源会被释放。那么我们这么判断一个对象是否被引用尼。sys.getrefcount(对象)。统计一个对象被引用的次数。当引用次数=0的时候所占内存就会被释放。

  • getrefcount 本身也会引入一次计数
  • 在函数调用发生的时候,会产生额外的两次引用,一次来自函数栈,另一个是函数参数
import sysa = []# 两次引用,一次来自 a,一次来自 getrefcount
print(sys.getrefcount(a))def func(a):# 四次引用,a,python 的函数调用栈,函数参数,和 getrefcountprint(sys.getrefcount(a))func(a)# 两次引用,一次来自 a,一次来自 getrefcount,函数 func 调用已经不存在
print(sys.getrefcount(a))/usr/local/bin/python3.7 /Users/zhonglinglong/PycharmProjects/whole_world/临时文件.py
2
4
2Process finished with exit code 0
  • a、b、c、d、e、f、g 这些变量全部指代的是同一个对象,而 sys.getrefcount() 函数并不是统计一个指针,而是要统计一个对象被引用的次数,所以最后一共会有八次引用。
import sysa = []print(sys.getrefcount(a)) # 两次b = aprint(sys.getrefcount(a)) # 三次c = b
d = b
e = c
f = e
g = dprint(sys.getrefcount(a)) # 八次/usr/local/bin/python3.7 /Users/zhonglinglong/PycharmProjects/whole_world/临时文件.py
2
3
8Process finished with exit code 0

四、手动回收垃圾释放内存

先调用 del a 来删除对象的引用;然后强制调用 gc.collect(),清除没有引用的对象,即可手动启动垃圾回收

import osimport gc
import psutildef show_memory_info(hint):pid = os.getpid()p = psutil.Process(pid)info = p.memory_full_info()memory = info.uss / 1024. / 1024print('{} ,占用内存: {} MB'.format(hint, memory))show_memory_info('变量创建前')a = [i for i in range(10000000)]show_memory_info('变量创建后')del a
gc.collect()show_memory_info('手动清除变量完成')
print(a)/usr/local/bin/python3.7 /Users/zhonglinglong/PycharmProjects/whole_world/临时文件.py
变量创建前 ,占用内存: 47.93359375 MB
变量创建后 ,占用内存: 437.41015625 MB
手动清除变量完成 ,占用内存: 50.88671875 MB
Traceback (most recent call last):File "/Users/zhonglinglong/PycharmProjects/whole_world/临时文件.py", line 25, in <module>print(a)
NameError: name 'a' is not definedProcess finished with exit code 1

五、特殊情况。引用次数为0.也不自动回收。

如果2个对象互相引用,并且不在被别的对象所引用。它们是不会被垃圾回收的

import os
import psutildef show_memory_info(hint):pid = os.getpid()p = psutil.Process(pid)info = p.memory_full_info()memory = info.uss / 1024. / 1024print('{} ,占用内存: {} MB'.format(hint, memory))def func():show_memory_info('创建变量前')a = [i for i in range(10000000)]b = [i for i in range(10000000)]show_memory_info('创建2个变量之后')a.append(b)b.append(a)func()
show_memory_info('函数执行完成')/usr/local/bin/python3.7 /Users/zhonglinglong/PycharmProjects/whole_world/临时文件.py
创建变量前 ,占用内存: 47.921875 MB
创建2个变量之后 ,占用内存: 823.671875 MB
函数执行完成 ,占用内存: 823.671875 MBProcess finished with exit code 0

但是我们还是可以手动的去回收

import osimport gc
import psutildef show_memory_info(hint):pid = os.getpid()p = psutil.Process(pid)info = p.memory_full_info()memory = info.uss / 1024. / 1024print('{} ,占用内存: {} MB'.format(hint, memory))def func():show_memory_info('创建变量前')a = [i for i in range(10000000)]b = [i for i in range(10000000)]show_memory_info('创建2个变量之后')a.append(b)b.append(a)func()
gc.collect() # 手动清除
show_memory_info('函数执行完成')/usr/local/bin/python3.7 /Users/zhonglinglong/PycharmProjects/whole_world/临时文件.py
创建变量前 ,占用内存: 47.9765625 MB
创建2个变量之后 ,占用内存: 823.73046875 MB
函数执行完成 ,占用内存: 51.00390625 MBProcess finished with exit code 0

六、对象引用次数自动垃圾回收原理

Python 使用标记清除(mark-sweep)算法和分代收集(generational),来启用针对循环引用的自动垃圾回收

  • 先来看标记清除算法。我们先用图论来理解不可达的概念。对于一个有向图,如果从一个节点出发进行遍历,并标记其经过的所有节点;那么,在遍历结束后,所有没有被标记的节点,我们就称之为不可达节点。显而易见,这些节点的存在是没有任何意义的,自然的,我们就需要对它们进行垃圾回收。
  • 当然,每次都遍历全图,对于 Python 而言是一种巨大的性能浪费。所以,在 Python 的垃圾回收实现中,mark-sweep 使用双向链表维护了一个数据结构,并且只考虑容器类的对象(只有容器类对象才有可能产生循环引用)
  • 而分代收集算法,则是另一个优化手段。Python 将所有对象分为三代。刚刚创立的对象是第 0 代;经过一次垃圾回收后,依然存在的对象,便会依次从上一代挪到下一代。而每一代启动自动垃圾回收的阈值,则是可以单独指定的。当垃圾回收器中新增对象减去删除对象达到相应的阈值时,就会对这一代对象启动垃圾回收。
  • 事实上,分代收集基于的思想是,新生的对象更有可能被垃圾回收,而存活更久的对象也有更高的概率继续存活。因此,通过这种做法,可以节约不少计算量,从而提高 Python 的性能。
  • 学了这么多,刚刚面试官的问题,你应该能回答得上来了吧!没错,引用计数是其中最简单的实现,不过切记,引用计数并非充要条件,它只能算作充分非必要条件;至于其他的可能性,我们所讲的循环引用正是其中一种。
  • 不过,虽然有了自动回收机制,但这也不是万能的,难免还是会有漏网之鱼。内存泄漏是我们不想见到的,而且还会严重影响性能。
  • objgraph,一个非常好用的可视化引用关系的包。在这个包中,我主要推荐两个函数,第一个是 show_refs(),它可以生成清晰的引用关系图。
  • 通过下面这段代码和生成的引用调用图,你能非常直观地发现,有两个 list 互相引用,说明这里极有可能引起内存泄露。这样一来,再去代码层排查就容易多了。
  • import objgraph

    a = [1, 2, 3]
    b = [4, 5, 6]

    a.append(b)
    b.append(a)

    objgraph.show_refs([a]

  • 而另一个非常有用的函数,是 show_backrefs()。下面同样为示例代码和生成图,你可以自己先阅读一下:
  • import objgraph

    a = [1, 2, 3]
    b = [4, 5, 6]

    a.append(b)
    b.append(a)

    objgraph.show_backrefs([a])

  • 相比刚才的引用调用图,这张图显得稍微复杂一些。不过,我仍旧推荐你掌握它,因为这个 API 有很多有用的参数,比如层数限制(max_depth)、宽度限制(too_many)、输出格式控制(filename output)、节点过滤(filter, extra_ignore)等。所以,建议你使用之前,先认真看一下
  • 垃圾回收是 Python 自带的机制,用于自动释放不会再用到的内存空间;
  • 引用计数是其中最简单的实现,不过切记,这只是充分非必要条件,因为循环引用需要通过不可达判定,来确定是否可以回收;
  • Python 的自动回收算法包括标记清除和分代收集,主要针对的是循环引用的垃圾收集;
  • 调试内存泄漏方面, objgraph 是很好的可视化分析工具。

Python3.7学习笔记30-垃圾回收机制相关推荐

  1. .NET深入学习笔记(3):垃圾回收与内存管理

    今天抽空来讨论一下.Net的垃圾回收与内存管理机制,也算是完成上个<WCF分布式开发必备知识>系列后的一次休息吧.以前被别人面试的时候问过我GC工作原理的问题,我现在面试新人的时候偶尔也会 ...

  2. Python3.5源码分析-垃圾回收机制

    Python3源码分析 本文环境python3.5.2. 参考书籍<<Python源码剖析>> python官网 Python3的垃圾回收概述 随着软硬件的发展,大多数语言都已 ...

  3. java垃圾回收机制_笔记 | Java垃圾回收机制

    本文经授权转载自程序员杂货铺(ID:speakFramework) 垃圾回收 最近上海的小伙伴是不是要被强垃圾分类搞疯了???哈哈哈哈 上海是个走在前列的城市啊,不光骑自行车闯红灯要被罚钱,垃圾不分类 ...

  4. Java基础笔记(二)垃圾回收机制

    内存管理 Java的内存管理很大程度指的就是对象的管理,其中包括对象空间的分配和释放. 对象空间的分配:使用new关键字创建对象即可 对象空间的释放:将对象赋值null即可.垃圾回收器将负责回收所有& ...

  5. JVM学习笔记之-垃圾回收相关概念 System.gc()的理解 内存溢出与内存泄漏 STW 垃圾回收的并行与并发 安全点与安全区域 再谈引用:强引用 软引用 弱引用 虚引用 终结器引用

    System.gc()的理解 在默认情况下,通过System.gc()或者Runtime. getRuntime ( ).gc ()的调用,会显式触发Full GC,同时对老年代和新生代进行回收,尝试 ...

  6. 【JVM学习笔记】垃圾回收基础篇

    今年就要去参加面试了,之前读完了<深入理解JAVA虚拟机>这本书,感觉并没有完全参透,现在重读这本书,希望能得到不一样的收获. 目录 Stop-The-World介绍 对象的年龄(垃圾分代 ...

  7. 判断是否存在此对象_JVM的垃圾回收机制,判断对象是否死亡

    这节我们主要讲垃圾收集的一些基本概念,先了解垃圾收集是什么.然后触发条件是什么.最后虚拟机如何判断对象是否死亡. 一.前言 我们都知道Java和C++有一个非常大的区别就是Java有自动的垃圾回收机制 ...

  8. java 堆_JAVA学习笔记 07——堆、栈和垃圾回收机制

    程序执行的内存分析过程: 虚拟机栈(简称:栈)的特点如下: 1. 栈描述的是方法执行的内存模型.每个方法被调用都会创建一个栈帧(存储局部变量.操作数.方法出口等). 2. JVM为每个线程创建一个栈, ...

  9. 4、JVM垃圾回收机制、新生代的GC、GC(Minor GC、FullGC)、GC日志、JVM参数选项、元空间(笔记)

    4.JVM垃圾回收机制 4.1.新生代的GC 4.1.1.串行GC(SerialGC) 4.1.2.并行回收GC(Parallel Scavenge) 4.1.3.并行GC(ParNew) 4.2.G ...

最新文章

  1. [css] 如何禁用移动的选择高亮?
  2. 云计算的 2020:云原生崛起,重新定义软件!
  3. 洛谷 P3320: bzoj 3991: LOJ 2182: [SDOI2015]寻宝游戏
  4. 习进度条2017上学期第十周
  5. Page.Validate()方法
  6. 安装 Tableau Desktop 时出现“0x80070109”错误以及Tableau Desktop破解
  7. 火狐浏览器怎么打不开网页
  8. Leetcode第904题
  9. opencv读取海康威视摄像头
  10. 传统蓝牙HCI Command(蓝牙HCI命令)详细介绍
  11. 使用MindStudio进行MindX SDK财务票据OCR识别开发
  12. 如何通俗解释Docker是什么?
  13. 带你学C带你飞 | printf函数 | 变量 | 常量和宏定义 | 数据类型 | 取值范围 | 字符串 | 运算符
  14. python读取压缩文件的大小_python查看zip包中文件及大小的方法
  15. 乔布斯那些经典的激励我们的语录
  16. 微信小程序之五星评分效果
  17. 学会这几项windows操作,轻松玩转自己的个人电脑
  18. cesium 加载geojson 贴3dtiles
  19. 大咖说·对话生态|当Confluent遇见云:实时流动的数据更有价值
  20. MediaCodec 错误整理

热门文章

  1. 2.格式化输出与输入
  2. 基于Java毕业设计阳光社区新冠瘦苗接种系统源码+系统+mysql+lw文档+部署软件
  3. 【MySQL】MySQL的数据类型
  4. 关于Trigger的介绍
  5. vscode使用vetur解决代码换行、格式化、常量问题
  6. 空气动力研究与发展中心计算机所,中国空气动力研究与发展中心报告.pdf
  7. 计算机数制详解及相互转换(二进制、八进制、十进制、十六进制)
  8. iphone SE 自带视频播放器要求的视频格式转换参数
  9. 程序员口中的demo是什么意思_怎样让5分钟的曲子不重样播放450天?程序员:用马尔可夫链...
  10. 深度学习 英文 训练阶段_深度学习英语单词才是王道丨6步就搞定!