python实现链表的删除_Python垃圾回收机制

236bf3b30aaef33fc54ea6d7c4395bb6.png

python作为一门解释型语言,以代码简洁易懂著称。我们可以直接对名称赋值,而不必声明类型。名称类型的确定、内存空间的分配与释放都是由python解释器在运行时进行的。python这一自动管理内存功能极大的减小了程序员负担,这也是成就python自身的重要原因之一。以下简要介绍一下Python的三种垃圾回收机制:

引用计数

Python中,主要通过引用计数(Reference Counting)进行垃圾回收。

typedef struct_object {

int ob_refcnt;

struct_typeobject *ob_type;

} PyObject;

在Python中每一个对象的核心就是一个结构体PyObject,它的内部有一个引用计数器(ob_refcnt)。程序在运行的过程中会实时的更新ob_refcnt的值,来反映引用当前对象的名称数量。当某对象的引用计数值为0,那么它的内存就会被立即释放掉。

以下情况是导致引用计数加一的情况:

对象被创建,例如a=2

对象被引用,b=a

对象被作为参数,传入到一个函数中

对象作为一个元素,存储在容器中

下面的情况则会导致引用计数减一:

对象别名被显示销毁 del

对象别名被赋予新的对象

一个对象离开他的作用域

对象所在的容器被销毁或者是从容器中删除对象

我们还可以通过sys包中的getrefcount()来获取一个名称所引用的对象当前的引用计数(注意,这里getrefcount()本身会使得引用计数加一)

sys.getrefcount(a)

引用计数法有其明显的优点,如高效、实现逻辑简单、具备实时性,一旦一个对象的引用计数归零,内存就直接释放了。不用像其他机制等到特定时机。将垃圾回收随机分配到运行的阶段,处理回收内存的时间分摊到了平时,正常程序的运行比较平稳。但是,引用计数也存在着一些缺点,通常的缺点有:

逻辑简单,但实现有些麻烦。每个对象需要分配单独的空间来统计引用计数,这无形中加大的空间的负担,并且需要对引用计数进行维护,在维护的时候很容易会出错。

在一些场景下,可能会比较慢。正常来说垃圾回收会比较平稳运行,但是当需要释放一个大的对象时,比如字典,需要对引用的所有对象循环嵌套调用,从而可能会花费比较长的时间。

循环引用。这将是引用计数的致命伤,引用计数对此是无解的,因此必须要使用其它的垃圾回收算法对其进行补充。

也就是说,Python 的垃圾回收机制,很大一部分是为了处理可能产生的循环引用,是对引用计数的补充。

标记清除(解决循环引用)

Python采用了“标记-清除”(Mark and Sweep)算法,解决容器对象可能产生的循环引用问题。(注意,只有容器对象才会产生循环引用的情况,比如列表、字典、用户自定义类的对象、元组等。而像数字,字符串这类简单类型不会出现循环引用。作为一种优化策略,对于只包含简单类型的元组也不在标记清除算法的考虑之列)

跟其名称一样,该算法在进行垃圾回收时分成了两步,分别是:

A)标记阶段,遍历所有的对象,如果是可达的(reachable),也就是还有对象引用它,那么就标记该对象为可达;

B)清除阶段,再次遍历对象,如果发现某个对象没有标记为可达,则就将其回收。

如下图所示,在标记清除算法中,为了追踪容器对象,需要每个容器对象维护两个额外的指针,用来将容器对象组成一个双端链表,指针分别指向前后两个容器对象,方便插入和删除操作。python解释器(Cpython)维护了两个这样的双端链表,一个链表存放着需要被扫描的容器对象,另一个链表存放着临时不可达对象。在图中,这两个链表分别被命名为”Object to Scan”和”Unreachable”。图中例子是这么一个情况:link1,link2,link3组成了一个引用环,同时link1还被一个变量A(其实这里称为名称A更好)引用。link4自引用,也构成了一个引用环。从图中我们还可以看到,每一个节点除了有一个记录当前引用计数的变量ref_count还有一个gc_ref变量,这个gc_ref是ref_count的一个副本,所以初始值为ref_count的大小。

aad73fd93c6f6fe4bef8962b4d01b068.png

gc启动的时候,会逐个遍历”Object to Scan”链表中的容器对象,并且将当前对象所引用的所有对象的gc_ref减一。(扫描到link1的时候,由于link1引用了link2,所以会将link2的gc_ref减一,接着扫描link2,由于link2引用了link3,所以会将link3的gc_ref减一…..)像这样将”Objects to Scan”链表中的所有对象考察一遍之后,两个链表中的对象的ref_count和gc_ref的情况如下图所示。这一步操作就相当于解除了循环引用对引用计数的影响。

d86363fa643d26f1ceab414756f604c9.png

接着,gc会再次扫描所有的容器对象,如果对象的gc_ref值为0,那么这个对象就被标记为GC_TENTATIVELY_UNREACHABLE,并且被移至”Unreachable”链表中。下图中的link3和link4就是这样一种情况。

96b8ea0b6fd28d724f723a2c0a500e58.png

如果对象的gc_ref不为0,那么这个对象就会被标记为GC_REACHABLE。同时当gc发现有一个节点是可达的,那么他会递归式的将从该节点出发可以到达的所有节点标记为GC_REACHABLE,这就是下图中link2和link3所碰到的情形。

30fa77d2e4e71a80faa95a22bfb2faf2.png

除了将所有可达节点标记为GC_REACHABLE之外,如果该节点当前在”Unreachable”链表中的话,还需要将其移回到”Object to Scan”链表中,下图就是link3移回之后的情形。

40dbadf37f98d0fb4e32990ae7300c1c.png

第二次遍历的所有对象都遍历完成之后,存在于”Unreachable”链表中的对象就是真正需要被释放的对象。如上图所示,此时link4存在于Unreachable链表中,gc随即释放之。

上面描述的垃圾回收的阶段,会暂停整个应用程序,等待标记清除结束后才会恢复应用程序的运行。

分代回收

在循环引用对象的回收中,整个应用程序会被暂停,为了减少应用程序暂停的时间,Python 通过“分代回收”(Generational Collection)以空间换时间的方法提高垃圾回收效率。

分代回收是基于这样的一个统计事实,对于程序,存在一定比例的内存块的生存周期比较短;而剩下的内存块,生存周期会比较长,甚至会从程序开始一直持续到程序结束。生存期较短对象的比例通常在 80%~90% 之间,这种思想简单点说就是:对象存在时间越长,越可能不是垃圾,应该越少去收集。这样在执行标记-清除算法时可以有效减小遍历的对象数,从而提高垃圾回收的速度。

python gc给对象定义了三种世代(0,1,2),每一个新生对象在generation zero中,如果它在一轮gc扫描中活了下来,那么它将被移至generation one,在那里他将较少的被扫描,如果它又活过了一轮gc,它又将被移至generation two,在那里它被扫描的次数将会更少。

gc的扫描在什么时候会被触发呢?答案是当某一世代中被分配的对象与被释放的对象之差达到某一阈值的时候,就会触发gc对某一世代的扫描。值得注意的是当某一世代的扫描被触发的时候,比该世代年轻的世代也会被扫描。也就是说如果世代2的gc扫描被触发了,那么世代0,世代1也将被扫描,如果世代1的gc扫描被触发,世代0也会被扫描。

该阈值可以通过下面两个函数查看和调整:

gc.get_threshold()# (threshold0, threshold1, threshold2).

gc.set_threshold(threshold0[, threshold1[, threshold2]])

下面对set_threshold()中的三个参数threshold0, threshold1, threshold2进行介绍。gc会记录自从上次收集以来新分配的对象数量与释放的对象数量,当两者之差超过threshold0的值时,gc的扫描就会启动,初始的时候只有世代0被检查。如果自从世代1最近一次被检查以来,世代0被检查超过threshold1次,那么对世代1的检查将被触发。相同的,如果自从世代2最近一次被检查以来,世代1被检查超过threshold2次,那么对世代2的检查将被触发。get_threshold()是获取三者的值,默认值为(700,10,10).

总结

总体来说:

  1. 在Python中,主要通过引用计数进行垃圾回收;
  2. 通过 “标记-清除” 解决容器对象可能产生的循环引用问题;
  3. 通过 “分代回收” 以空间换时间的方法提高垃圾回收效率。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/262207.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第一章:The Missing Code Library--2.合法化输入

合法化输入:只允许数字和字母 用户常常会忽略掉说明,并且输入错误的数据。作为一个Shell脚本开发人员,你需要拦截并纠正这些错误。 典型情况是,你或许会遇见文件名或是数据库的键。你提示用户要输入一个全部由大小写字母和数字…

java 缓存清理echo_“kill -9”一时爽,秋后算账泪两行

原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处。任何不保留此声明的转载都是抄袭。kill是杀死的意思,带有主动的意味。鉴于master、slave这样的名词,需要在计算机软件中进行整改&#xff…

解决stackoverflow打开慢不能注册登录

http://blog.csdn.net/dream_an/article/details/50280977 解决stackoverflow打开慢不能注册登录 标签: stack overflowfirefox扩展打不开 2015-12-13 09:16 131人阅读 评论(2) 收藏 举报 分类:综合(6) 作者同类文章X版权声明&a…

PerlRegex堆栈溢出的问题

PerlRegex一直用得很顺手,但今天晚上却老是出现堆栈溢出的问题,调整了Delphi 的Max stack size后,仍然解决不了。 后来发现是正则表达式的问题,具体细节问题还不太清楚 我一般使用“((.|\n)?)”来匹配多行文本,而出现…

《架构之美》学习随笔:好的架构

好的架构是很多因素的结果,包括以下方面: 1、确定进行有意为之的前端设计 2、设计者的素质和经验 3、在开发过程中,保持清晰的设计观点 4、授权团队负责软件的整体设计,而团队也承担起这一责任 5、不要害怕改变设计:没…

halcon 旋转_HALCON高级篇:3D相机标定(3/3)

访问标定结果算子calibrate_cameras的主要结果由相机内参和每一张图像标定板的位姿组成。算子将它们存储在标定数据模型中,可以用算子get_calib_data来访问它们。相机外参并不能直接被获取,因为所需的世界坐标系统的信息没有存储在标定数据模型中。然而&…

java实现poi导入excel_Java POI实现将导入Excel文件的示例代码

问题描述现需要批量导入数据,数据以Excel形式导入。POI介绍我选择使用的是apache POI。这是有Apache软件基金会开放的函数库,他会提供API给java,使其可以对office文件进行读写。我这里只需要使用其中的Excel部分。实现首先,Excel有…

JS中双引号单引号,转义字符问题!!

investListHtml <div class"targetBlock"> <p> targetClass <span>总金额 <i> loanAmount </i> 元&#xff0c;剩余可投 <i> (loanAmount-collectAmount) </i> 元</span></p> <div class"targetCon…

什么叫n+1次select查询问题?

在Session的缓存中存放的是相互关联的对象图。默认情况下&#xff0c;当Hibernate从数据库中加载Customer对象时&#xff0c;会同时加载所有关联的Order对象。以Customer和Order类为例&#xff0c;假定ORDERS表的CUSTOMER_ID外键允许为null&#xff0c;图1列出了CUSTOMERS表和O…

OGRE 入门 二、Basic Tutorial 1 : An introduction to the most basic Ogre constructs

1. 下载源代码及脚本 这里有一个‘Convenient All-In-One’ 版的框架。 2. 创建场景 解压clean_ogre_cmake_project.zip&#xff0c;修改TutorialApplication.cpp中的函数&#xff1a; 1 void TutorialApplication::createScene(void)2 {3 // create your scene here :)4 …

React Native 重新建项目遇到的一些问题

1、基本上一句话&#xff0c;就是本地的node太旧了&#xff0c;跟不上React_Native的节奏&#xff0c;所以需要更新node&#xff0c;但是单纯的更新node丫丫竟然不让我跟&#xff0c;因为是用Homebrew来管理的&#xff0c;所以先update了下brew brew update && brew up…

picACG本地缓存目录_手机上本地存储的哪些文件、文件夹不能删?

手机、电脑随着不断的使用&#xff0c;系统本身、系统工具、第三方应用都会不断产生一些临时文件和垃圾文件。手机产生的垃圾文件更是显得杂乱无章&#xff0c;虽然可以使用系统自带或第三方应用来清理垃圾&#xff0c;但在本地存储还是有大量的文件夹或文件是可以手动删除的。…

python元组和列表的联系_Python元组与列表的区别和联系?

1.元组和列表比较相似&#xff0c;不过它们之间也有着不同&#xff1a; &#xff08;1&#xff09;列表&#xff1a;一个大仓库&#xff0c;你可以随时往里边添加和删除任何东西。 &#xff08;2&#xff09;元组&#xff1a;封闭的列表&#xff0c;一旦定义&#xff0c;就不可…

java的日期操作_java中对时间的操作详解

代码演示&#xff1a;//数据库中去的日期Date s list.get(0).getSdate();System.out.println(s);// Tue Apr 28 00:00:00 CST 2015//手动newDate d new Date();System.out.println(d);// Tue Apr 28 20:53:15 CST 2015System.out.println("" s.after(d));//使用Ca…

1个多月就能看到效果的减肥大法 - 健康程序员,至尚生活!

减肥方法很多。选择减肥方法时应以物理减肥和减少饮食为主。不应该以口服药物为主。常用减肥方法有&#xff1a; ① 预防性减肥&#xff1b; ②运动减肥&#xff1b; ③行为减肥&#xff1b; ④机械减肥&#xff1b; ⑤ 桑那浴减肥&#xff1b; ⑥石膏减肥&#xff1b; ⑦石腊减…

pytorch自带网络_PyTorch机器学习笔记(1)整好环境

2020年1月1日炼丹第0步&#xff0c;装好环境系统 Ubuntu 18.04 LTS先装上conda&#xff08;自带大多数科学计算基础包&#xff0c;以及比较优秀的包管理系统&#xff09;&#xff1a;国内网络环境建议到清华大学镜像站下载&#xff1a;Tsinghua Open Source Mirror​mirror.tun…

Atom与markdown

简述 Atom是github开发的开源跨平台的编辑器&#xff0c;Atom的强大可以与大名鼎鼎的Sublime Text相媲美。因为使用过Sublime Text&#xff0c;所以用Atom上手很快。这篇文章主要介绍使用Atom写markdown。之前在项目开发中都是使用.doc文件作为接口文档的载体&#xff0c;但是在…

java 正则匹配 sql星号,正则表达式匹配星号和换行符之间的字符串

Example:blah blah * Match this text Match this textMatch this textMatch this textMatch this text*more text more textHow to get string from inside the asterisk with the line breaks?解决方案You can use a negated match here. Notice that I escaped \ the liter…

Effective C# Item22:使用事件定义外发接口

事件为类型定义了外发接口&#xff0c;C#的事件是建立在委托的基础上的&#xff0c;委托为事件处理器提供了类型安全的函数签名。 委托要比事件的使用范围广泛&#xff0c;我们可以把事件看做是一种经过了封装的委托&#xff0c;专门用于事件驱动模型。你可以在客户代码中直接调…

【VC6.0】getline需要输入2次回车才会结束的BUG修复方法

今天看CPrimer的时候发现一个问题&#xff0c;getline需要输入2次回车才会显示结果&#xff0c;上网找了一下&#xff0c;发现是VC6.0的原因&#xff0c;修复原因如下&#xff1a; &#xff08;1&#xff09;建立一个1.CPP &#xff08;2&#xff09;输入#include <string&g…