python---核心知识8之对象的生命周期以及内存管理机制

生命周期的概念:世界上的万事万物都有它的生命周期,那么针对对象的生命周期到底是从哪里开始从哪里结束呢?当我们创建一个对象时,会自动分配一个内存地址给这个对象使用,这就是生命周期的开始,当我们不使用这个对象时,就会回收这个对象,并释放对象所占用的内存地址,这就是对象的消亡;

那么针对对象的生命周期我们应该如何监控呢?主要就是3个方法,首先创建对象的时候,会执行__new__方法,当对象创建成功时会调用__init__这个初始化方法,当对象被回收的时候会调用__del__方法,我们主要就是通过这3个方法来监控对象的生命周期:

class Person:

    # def __new__(cls, *args, **kwargs):

        # print('我被创建了')

    def __init__(self):

        print('创建成功了')

    def __del__(self):

        print('我被删除了')

p=Person()

结果:

创建成功了

我被删除了

生命周期小案例:

在讲生命周期小案例之前我们先补充两个小的知识点,就是global和nonlocal这两个关键字,global是修饰全局变量的,当用global修饰之后,我们在局部进行赋值操作的时候,就会把局部赋值的这个变量当成全局变量,nonlocal这个关键字是修饰函数的变量,特别需要注意的是nonlocal修饰类属性是起不到作用的,nonlocal只能作用于函数的变量,当我们用这个关键字修饰的时候,那么在内函数给变量赋值就会把这个变量当成外函数所定义的变量:

global的使用:

num=5

def text_global():

    global num

    num=6

    print(num)

text_global()

nonlocal的使用:

现在我们在来看看生命周期的小案例:比如我们需要统计一个班级的学生,当增加一个学生,或者减少一个学生我们就把学生数输出出来,分析:首先肯定需要一个学生类,然后当创建一个对象,或者减少一个对象我们都需要输出当前对象的个数:

下面就把代码贴出来:我们来分析一下代码,首先学生的增减时外部不能控制的,所以我们这个属性应该是私有属性,是由类本身决定的,当实例出一个对象会调用__init__方法,当回收一个对象会调用__del__方法,所以我们在这两个方法里面去操控数据以及输出,这里需要注意一点是属性的查找操作和赋值操作有不同的机制,比如说我们用实例查找属性时首先会在自身的__dict__去找,如果没有就会去__class__对应的类去查找,但是赋值操作不一样,因为赋值操作有两种含义,一种是修改值,一种是增加属性,比如说我们给实例属性一个赋值操作,如果实例有这个属性的话就是修改,如果实例没有这个属性的话就是给这个实例增加这个属性,并不是继续往它的class去找,我们需要记住赋值操作永远都是针对自己,而不会去找其他人,就好比你有一百万,你肯定会自己留着,而不会给别人一个道理,那么我们给类的属性赋值,直接用类去点,或者通过实例找到类再去操作,好了,扯远了,就酱;

内存管理机制:

首先我们从存储的层面去看,1.在python里面万物皆对象,不像java里面有什么基本数据类型,而python里面不存在基本数据类型,1,2,'a',等都是对象;2.所有的对象在内存里面都会开辟一块空间给它存储,解释器会根据不同的类型以及大小开辟不同的空间存储,在返回空间的存储地址以便于外界对这个对象的操作,3.对于整数和短小的字符,python会对其进行缓存,不会创建多个相同的对象,4.对于集合,列表等容器对象存储的其他对象仅仅是存储的其他对象的引用,并不是对象本身;

我们只验证一下python的缓存,其他没啥可验证的:


接下来我们来看看垃圾回收方面:说到垃圾回收,肯定会涉及到引用计数器的概念,所谓引用计数器就是对当前对象被引用的个数,当增加引用的时候,这个引用计数器就会自动加一,当减少一个引用的时候,这个引用计数器就会减一,举例看看:

从下面的代码来看,获取引用个数是通过sys模块的getrefcount()函数,里面直接传对象,并且需要注意的是当用getrefcount()获取引用个数的时候,会比真正的引用个数大一,因为当前也有一个正在被getrefcount用着;但是当这个函数执行完之后,那么它的引用也会被释放,下面我们再来看看什么情况会增加:1.当我们实例出对象的时候,2.当这个对象被赋值引用的时候,3.当对象被函数引用的时候,特别需要注意的是被函数引用个数是加2,4.当被容器当成元素的时候,那么什么情况又会减少呢?1.使用del删除的时候,2.函数执行完毕的时候,3.容量被删除或者容器内该元素被移除,

我们在来分析一下为什么对象传进函数会增加两次呢?这两次分别是哪里:通过代码可以看到person1在text函数的属性里出现过两次,分别是__globals__和func_globals,所以对象被函数调用计数器会加2


引用计数器循环引用的问题:比如说有这样的一个场景,有一个teacher类有一个student类,teacher类有一个属性指向student,而student也有个属性指向teacher,在这种情况下当我们分别删除实例出的对象之后,那属性指向的对象会被回收吗?我们看代码:这里我们安装了一个objgraph模块,里面有个方法count统计的是垃圾回收器跟踪的对象个数,说白了就是当前实例出来的对象的个数,从下面的代码我们可以看到当我们把s和t实例删除之后,实例对象仍然存在,这就造成了内存泄漏,那针对这种问题该怎么解决呢?其实python的内存管理机制由两部分组成:计数器机制和垃圾回收机制;

垃圾回收机制:其实一般情况下的垃圾通过引用计数器都能回收,垃圾回收机制是针对于通过了引用计数器还存活的对象,在找到循环引用的对象,最后在释放它;

垃圾回收机制的原理:

1.通过引用计数器还能存活的垃圾其实就是被循环引用的对象,所以我们第一步肯定是要找到所有被循环引用的对象,能被循环引用的对象其实都是容器对象,容器对象指的就是能装对象的容器,比如说字典,列表,等等,你想想像int这种对象能产生循环引用的效果吗,产生循环引用就是因为互相都有属性指向对方,在说明白一点就是互相都能装其他对象,那int能装其他对象吗,明显是不能的,所以我们第一步就是找到所有的容器对象,找到了所有的容器对象,肯定要找个东西存下来啊,并且这个东西要很灵活方便的处理里面装的东西,所以选择了双向链表,对于双向链表的优点自行百度;

2.找到了容器变量之后,自然是看这个对象有多少个引用,大家如果不好理解,其实就可以把容器对象理解成普通的没被回收的对象,底层是把引用的数字存在了变量gc_refs里面;

3.对于每个容器对象a,要找到它所引用的容器对象b,并且把容器对象b的引用数减一,这一步就是最重要的,相当于把互相引用的那条引用去掉;

4.通过了第3步,如果有容器对象的引用数为0.证明它肯定是被循环引用才活到现在的,所以就把这个对象回收掉;

垃圾回收机制的优化:比如说我们有一万个对象都是存活的,每隔5s进行一次检测,总不能每次都去检测一万个吧,这样是很耗时的,性能会降低很多,所以python使用的是分代回收,什么是分代回收呢?比如说我们总共有一百个对象,在经历了10次检测之后,有50个对象还是存活的,那么我们就把这50个对象分为一代,在进行重新检测的时候我们就不用检测这50个对象。当检测到一定次数的时候,会促发检测一次这50个对象,也就是一代,当一代经历过10次检测之后还剩5个存活对象,那就把这5个存活对象分为二代,当一代检测到一定次数就会促发第二代的检测,如此类推,其实就有点像是差生和优生之间的检测,在经过100次考试之后,有10个一直都是优生,那么这10个人接下来就可以不用进行测试,当继续测试到一定次数之后,这10个人才又参加一次测试,这是这个道理;

垃圾回收机制配置参数:我们的垃圾回收机制除了使用分代回收之外,还在是否促发检测上面给定了一个阈值,只有当目前存在的对象达到这个阈值时才会促发检测;我们通过gc模块可以得到这个阈值,也能修改这个阈值:我们可以看到下面的代码,一共有三个参数,第一个是阈值,第二个是促发一代检测的次数,第三个是促发二代检测的次数;

垃圾回收机制自动回收需要的条件:

1.垃圾回收机制是打开的;垃圾回收可以通过gc模块来进行打开或者关闭,也可以判断当前垃圾回收是否打开;默认情况下是打开的;

2.达到指定的阈值;


垃圾回收机制的手动触发:我们知道垃圾回收机制在一定的条件下会自动促发,但是这个条件往往比较高,有时候需要手动促发,手动促发用的是gc模块的collect方法,这个方法有一个选填参数,默认不填是检测0,1,2代的垃圾,填1检测的是0和1代的垃圾,填2检测的是0,1,2,的垃圾;我们可以看下面的代码通过gc.collect()把之前不能回收的循环引用的对象都回收了;

垃圾回收机制解决循环引用的兼容问题:如下代码所示,我们把python版本改成了2.7,并且重写了类的del方法,我们会发现gc.collect()函数不管用了,还是回收不到这两个对象,接下来我们就来解决这样的问题

解决方案1:通过弱引用来代替强引用,引用对象,所谓弱引用其实就是当我们使用弱引用引用一个对象时,这个对象的refcount不会增加,这样的话就不会造成循环引用,自然就解决了;我们看下面的代码,弱引用用的是weakref模块下的ref方法;在补充一点获取弱引用字典可以使用weakref.WeakKeyDictionary这个是字典的key全是弱引用以及weakref.WeakValueDictionary这个是字典的值全是弱引用;


解决方案2:就是我们手动把他们之一的引用给释放掉,就相当于是置空,这样也就没有了循环引用:


至此,这一大块就结束了,讲的不对的地方,希望大家给出指正的意见;

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容

  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,246评论 11 349
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,654评论 18 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,630评论 18 399
  • 今天早上起来读英语,感觉自己读得好没劲。 还不如《英语流利说》里面的练习。 然后,遇见了师姐。 我告诉她,我在准备...
    减肥的女孩阅读 188评论 0 0
  • 现在是2015年10月31日15点21分,在上养生康复学的选修课,耳机外是学生在讲自己的作业,和秋冬养生有关,我的...
    一碗糯米饭阅读 285评论 0 1