python垃圾回收机制、内存管理_python的内存管理和垃圾回收机制-程序员宅基地

技术标签: python  

一、垃圾回收机制

python中对于垃圾回收机制总结的一句话为:引用计数器为主、标记清除和分代回收为辅

 1、双向环状链表refchain

        我们知道python是用C语言编写的,在python的底层维护着一个双向环状链表refchain,该链表中存储着我们在python中创建的所有变量指向的对象,也就是说当我们在python中创建一个变量时并赋值时,就会在该链表中添加一个对象。而该对象是一个结构体。结构体中存储着该对象的信息:

  • _ob_next:上一个对象
  • _ob_prev:下一个对象
  • ob_type:对象类型
  • ob_refcnt:被引用的次数
  • ob_value:值

2、引用计数器

在refchain中的所有对象内部都有一个ob_refcnt用来保存当前对象的引用计数器,顾名思义就是自己被引用的次数,如下:

num = 1
str1 ='sdf'
str2 =st1
如上,内存中有值1,sdf,他们的引用计数器分别为1,2

当值被多次引用时候,不会在内存中重复创建数据,而是引用计数器+1 。

当对象被销毁时候同时会让引用计数器-1

如果引用计数器为0,则将对象从refchain链表中摘除,同时在内存中进行销毁(暂不考虑缓存等特殊情况)。

  • 引用计数器+1的情况
    • 对象被创建
    • 另外的变量也指向当前对象
    • 作为容器对象的一个元素(如list)
    • 作为参数传递为函数
  • 引用计数器-1的情况
    • 变量被显式的销毁(如:del 变量)
    • 指向当前对象的变量重新赋值
    • 从容器中移除
    • 函数执行完成 
age = 18
number = age  # 对象18的引用计数器 + 1
del age          # 对象18的引用计数器 - 1
def run(arg):
    print(arg)
run(number)   # 刚开始执行函数时,对象18引用计数器 + 1,当函数执行完毕之后,对象18引用计数器 - 1 。
num_list = [11,22,number] # 对象18的引用计数器 + 1

 3、标记清除

标记清除的目的:

基于引用计数器进行垃圾回收非常方便和简单,但他还是存在循环引用/交叉感染的问题,导致无法正常的回收一些数据,例如:

l1 = [1,2,3] # refchain中创建一个列表对象,计数器为1.
l2 = [4,5,6] # refchain中再创建一个列表对象,引用计数器为1.
l1.append(l2)   #把l2追加到l1中,l2指向的对象的引用计数器+1,最终为2
l2.append(l1)   #把l1追加到l2中,l1指向的对象的引用计数器+1,最终为2
del l1          # 引用计数器-1
del l2          # 引用计数器-1

对于上述代码会发现,指向del操作之后,没有变量指向那两个列表对象,但由于循环引用的问题,他们的引用计数器不为0,所以他们的状态:永远不会被使用、也不会被销毁。项目中如果这种代码太多,就会导致内存一直被消耗,直到内存被耗尽,程序崩溃。

为了解决循环引用的问题,引入了标记清除技术,专门针对那些可能存在循环引用的对象进行特殊处理,可能存在循环应用的类型有:列表、元组、字典、集合、自定义类等那些能进行数据嵌套的类型。

标记清除的作用:

创建特殊链表专门用于保存 列表、元组、字典、集合、自定义类等,之后再去检查这个链表中的对象是否存在循环引用,如果存在则让双方的引用计数器均 - 1  ,此时对于垃圾回收存在两个两个链表

  •   1、存储所有定义变量指向对象的双向环状链表refchain
  •   2、存储可能存在循环引用的列表、元组、字典、集合、自定义类等对象的链表。
  • 注意(如列表等可能存在循环引用的对象既会存储在双向环状链表中也会存储在该链表中

 标记清除的缺点:

1、标记清除只会在python内存触发某种条件后才会被作用(即什么时候扫描链表?)

2、标记清除会循环整个链表,并去检查是否存在循环引用,对此时间效率非常低

4、分代回收 

分代回收的目的:解决标记清除的两个缺点

分代回收的作用:

对标记清除中的链表进行优化,将那些可能存在循环引用的对象拆分到3个链表,链表分为:0/1/2三代,每代都可以存储对象和阈值,当达到阈值时,就会对相应的链表中的每个对象做一次扫描,将有循环引用的对象的计数器各自减1并且销毁引用计数器为0的对象

三个链表的阈值:

  • 0代链表:0代链表中的对象达到700个时扫描一次
  • 1代链表:0代链表被扫描10次时,1代链表扫描一次
  • 2代链表:1代链表被扫描10次时,2代链表扫描一次

三个链表中都有两个属性:count,threshold:

  • 0代,count表示0代链表中对象的数量,threshold表示0代链表对象个数阈值,超过则执行一次0代扫描检查。
  • 1代,count表示0代链表扫描的次数,threshold表示0代链表扫描的次数阈值,超过则执行一次1代扫描检查。
  • 2代,count表示1代链表扫描的次数,threshold表示1代链表扫描的次数阈值,超过则执行一2代扫描检查。

情景模拟 :

根据C语言底层并结合图来讲解内存管理和垃圾回收的详细过程。

第一步:当创建对象age=19时,会将对象添加到refchain链表中。

第二步:当创建对象num_list = [11,22]时,会将列表对象添加到 refchain 和 generations 0代中。

第三步:新创建对象的数量达到0代链表上的对象数量大于阈值700时,要对0代对象链表上的对象进行扫描检查。

当0代链表中存储对象的数量大于阈值后,底层不是直接扫描0代,而是先判断2、1是否也超过了阈值。

  • 如果2、1代未达到阈值,则扫描0代,并让1代的 count + 1 。
  • 如果2代已达到阈值,则将2、1、0三个链表拼接起来进行全扫描,并将2、1、0代的count重置为0.
  • 如果1代已达到阈值,则讲1、0两个链表拼接起来进行扫描,并将所有1、0代的count重置为0.

5、垃圾回收机制的总结:

        在python中维护着一个双向环状链表refchain,这个链表存储着我们在程序中创建的所有变量指向的对象,每个对象都维护着一个计数器,如果该对象被其他对象引用则计数器加一,如果引用该对象的变量被销毁或者被重新赋值,则计数器减。最后当计数器为0时,则从refchain中移除,内存中销毁该对象,释放内存。

        但是对于 列表、字典等可以由多个元素组成的对象之间可能存在循环引用/交叉感染的问题,为了解决这个问题,python内部又引入了清除标记、分代回收的技术。在python底层划分出四个链表。refchain、0代链表、1代链表、2代链表。refchain中存储所有的对象,而0,1,2代对象中存储可能存在循环引用的对象。如果达到0、1、2代链表的阈值时则会扫描链表查看是否存在循环引用,如果存在则涉及的对象的计数器减一。

二、内存管理

从上文大家可以了解到当对象的引用计数器为0时,就会被销毁并释放内存。而实际上他不是这么的简单粗暴,因为反复的创建和销毁会使程序的执行效率变低。Python中引入了“缓存机制”机制。
例如:引用计数器为0时,不会真正销毁对象,而是将他放到一个名为free_list的链表中,之后会再创建对象时不会在重新开辟内存,而是在free_list中将之前的对象来并重置内部的值来使用

1、float类型

 维护的free_list链表最多可缓存100个float对象 

num = 3.14 #开辟内存来存储float对象,并将对象添加到refchain链表。
del num  #引用计数器-1,如果为0则在rechain链表中移除,不销毁对象,而是将对象添加到float的free_list中.
num2 = 2.5 # 此时不会开辟新的内存空间,而是去float的free_list中取出一个对象(内存地址空间),赋值并使用

         优先去free_list中获取对象,并重置为值为2.5,如果free_list为空才重新开辟内存。
注意:引用计数器为0时,会先判断free_list中缓存个数是否满了,未满则将对象缓存,已满则直接将对象销毁。以下的list、tupel、dict的free_list皆是如此

2、 list类型

维护的free_list数组最多可缓存80个list对象。

>>> li = [1,2,3]
>>> id(li)
2220235498688
>>> del li
>>> li2=['adf','csadf' ]
>>> id(li2)
2220235498688
>>>

3、tuple类型

维护一个free_list数组且数组容量20,数组中元素可以是链表且每个链表最多可以容纳2000个元组对象。元组的free_list数组在存储数据时,是按照元组可以容纳的个数为索引找到free_list数组中对应的链表,并添加到链表中。如下:

>>> tup1=(1,2) 
>>> id(tup1)
2220238433664
>>> del tup1  # 因为元组的数量为2,所以在删除该变量时,会把这个能存储2个元组的对象添加到free_list[2]的列表中
>>> tup2 =(3,4) # 不会重新的开辟新的内存地址来存储当前的元组,而是去维护元组的free_list[2]中取出一个对象(内存地址)赋值并使用
>>> id(tup2)
2220238433664
>>>

4、dict

维护的free_list数组最多可缓存80个dict对象

>>> dict1 = {'a':1}
>>> id(dict1)
2220235380672
>>> del dict1
>>> dict2 = {'b':2}
>>> id(dict2)
2220235380672
>>>

5、int (小内存池)

对于int类型,这里比较特殊,python底层并不是为此维护一个free_list。而是在编译器启动的时候,就会在内存中创建存储着[-5,256]的对象(内存地址空间),而这些对象的引用计数器永远不会为零,所以永远不会被销毁或者被添加到链表中。当我需要定义一个[-5,256]的对象时,如num=5,在内存中不会开辟新的内存,而是直接将内存中存储着5的那个对象返回

>>> num1 =-5
>>> id(-5)
2220233681008
>>> del num1
>>> num2 = -5
>>> id(num2)
2220233681008
>>>

对于除开[-5,256]的整型数字,则是声明即开辟空间存储 

>>> num2= 257
>>> id(num2)
2220238243440
>>> num3 =257
>>> id(num3)
2220238243472
>>>

6、str类型 

情况1:

如同int类型一样,内部将所有的ascii码缓存起来,以后使用时就不再反复创建

>>> str1='A'
>>> id(str1)
2220235792688
>>> str2 ='A'
>>> id(str2)
2220235792688
>>>

 情况2:

Python内部还对字符串做了驻留机制,针对那些只含有字母、数字、下划线的字符串,如果内存中已存在则不会重新再创建而是使用原来的地址里(不会像free_list那样一直在内存存活,只有内存中有才能被重复利用)。

>>> str1 ='python'
>>> id(str1)
2220238443248
>>> str2 ='python'
>>> id(str2)
2220238443248
>>> str3='pytho'
>>> id(str3)
2220238443312
>>>

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/adminwg/article/details/127258874

智能推荐

分布式光纤传感器的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告_预计2026年中国分布式传感器市场规模有多大-程序员宅基地

文章浏览阅读3.2k次。本文研究全球与中国市场分布式光纤传感器的发展现状及未来发展趋势,分别从生产和消费的角度分析分布式光纤传感器的主要生产地区、主要消费地区以及主要的生产商。重点分析全球与中国市场的主要厂商产品特点、产品规格、不同规格产品的价格、产量、产值及全球和中国市场主要生产商的市场份额。主要生产商包括:FISO TechnologiesBrugg KabelSensor HighwayOmnisensAFL GlobalQinetiQ GroupLockheed MartinOSENSA Innovati_预计2026年中国分布式传感器市场规模有多大

07_08 常用组合逻辑电路结构——为IC设计的延时估计铺垫_基4布斯算法代码-程序员宅基地

文章浏览阅读1.1k次,点赞2次,收藏12次。常用组合逻辑电路结构——为IC设计的延时估计铺垫学习目的:估计模块间的delay,确保写的代码的timing 综合能给到多少HZ,以满足需求!_基4布斯算法代码

OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版-程序员宅基地

文章浏览阅读3.3k次,点赞3次,收藏5次。OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版

关于美国计算机奥赛USACO,你想知道的都在这_usaco可以多次提交吗-程序员宅基地

文章浏览阅读2.2k次。USACO自1992年举办,到目前为止已经举办了27届,目的是为了帮助美国信息学国家队选拔IOI的队员,目前逐渐发展为全球热门的线上赛事,成为美国大学申请条件下,含金量相当高的官方竞赛。USACO的比赛成绩可以助力计算机专业留学,越来越多的学生进入了康奈尔,麻省理工,普林斯顿,哈佛和耶鲁等大学,这些同学的共同点是他们都参加了美国计算机科学竞赛(USACO),并且取得过非常好的成绩。适合参赛人群USACO适合国内在读学生有意向申请美国大学的或者想锻炼自己编程能力的同学,高三学生也可以参加12月的第_usaco可以多次提交吗

MySQL存储过程和自定义函数_mysql自定义函数和存储过程-程序员宅基地

文章浏览阅读394次。1.1 存储程序1.2 创建存储过程1.3 创建自定义函数1.3.1 示例1.4 自定义函数和存储过程的区别1.5 变量的使用1.6 定义条件和处理程序1.6.1 定义条件1.6.1.1 示例1.6.2 定义处理程序1.6.2.1 示例1.7 光标的使用1.7.1 声明光标1.7.2 打开光标1.7.3 使用光标1.7.4 关闭光标1.8 流程控制的使用1.8.1 IF语句1.8.2 CASE语句1.8.3 LOOP语句1.8.4 LEAVE语句1.8.5 ITERATE语句1.8.6 REPEAT语句。_mysql自定义函数和存储过程

半导体基础知识与PN结_本征半导体电流为0-程序员宅基地

文章浏览阅读188次。半导体二极管——集成电路最小组成单元。_本征半导体电流为0

随便推点

【Unity3d Shader】水面和岩浆效果_unity 岩浆shader-程序员宅基地

文章浏览阅读2.8k次,点赞3次,收藏18次。游戏水面特效实现方式太多。咱们这边介绍的是一最简单的UV动画(无顶点位移),整个mesh由4个顶点构成。实现了水面效果(左图),不动代码稍微修改下参数和贴图可以实现岩浆效果(右图)。有要思路是1,uv按时间去做正弦波移动2,在1的基础上加个凹凸图混合uv3,在1、2的基础上加个水流方向4,加上对雾效的支持,如没必要请自行删除雾效代码(把包含fog的几行代码删除)S..._unity 岩浆shader

广义线性模型——Logistic回归模型(1)_广义线性回归模型-程序员宅基地

文章浏览阅读5k次。广义线性模型是线性模型的扩展,它通过连接函数建立响应变量的数学期望值与线性组合的预测变量之间的关系。广义线性模型拟合的形式为:其中g(μY)是条件均值的函数(称为连接函数)。另外,你可放松Y为正态分布的假设,改为Y 服从指数分布族中的一种分布即可。设定好连接函数和概率分布后,便可以通过最大似然估计的多次迭代推导出各参数值。在大部分情况下,线性模型就可以通过一系列连续型或类别型预测变量来预测正态分布的响应变量的工作。但是,有时候我们要进行非正态因变量的分析,例如:(1)类别型.._广义线性回归模型

HTML+CSS大作业 环境网页设计与实现(垃圾分类) web前端开发技术 web课程设计 网页规划与设计_垃圾分类网页设计目标怎么写-程序员宅基地

文章浏览阅读69次。环境保护、 保护地球、 校园环保、垃圾分类、绿色家园、等网站的设计与制作。 总结了一些学生网页制作的经验:一般的网页需要融入以下知识点:div+css布局、浮动、定位、高级css、表格、表单及验证、js轮播图、音频 视频 Flash的应用、ul li、下拉导航栏、鼠标划过效果等知识点,网页的风格主题也很全面:如爱好、风景、校园、美食、动漫、游戏、咖啡、音乐、家乡、电影、名人、商城以及个人主页等主题,学生、新手可参考下方页面的布局和设计和HTML源码(有用点赞△) 一套A+的网_垃圾分类网页设计目标怎么写

C# .Net 发布后,把dll全部放在一个文件夹中,让软件目录更整洁_.net dll 全局目录-程序员宅基地

文章浏览阅读614次,点赞7次,收藏11次。之前找到一个修改 exe 中 DLL地址 的方法, 不太好使,虽然能正确启动, 但无法改变 exe 的工作目录,这就影响了.Net 中很多获取 exe 执行目录来拼接的地址 ( 相对路径 ),比如 wwwroot 和 代码中相对目录还有一些复制到目录的普通文件 等等,它们的地址都会指向原来 exe 的目录, 而不是自定义的 “lib” 目录,根本原因就是没有修改 exe 的工作目录这次来搞一个启动程序,把 .net 的所有东西都放在一个文件夹,在文件夹同级的目录制作一个 exe._.net dll 全局目录

BRIEF特征点描述算法_breif description calculation 特征点-程序员宅基地

文章浏览阅读1.5k次。本文为转载,原博客地址:http://blog.csdn.net/hujingshuang/article/details/46910259简介 BRIEF是2010年的一篇名为《BRIEF:Binary Robust Independent Elementary Features》的文章中提出,BRIEF是对已检测到的特征点进行描述,它是一种二进制编码的描述子,摈弃了利用区域灰度..._breif description calculation 特征点

房屋租赁管理系统的设计和实现,SpringBoot计算机毕业设计论文_基于spring boot的房屋租赁系统论文-程序员宅基地

文章浏览阅读4.1k次,点赞21次,收藏79次。本文是《基于SpringBoot的房屋租赁管理系统》的配套原创说明文档,可以给应届毕业生提供格式撰写参考,也可以给开发类似系统的朋友们提供功能业务设计思路。_基于spring boot的房屋租赁系统论文