Python垃圾回收学习笔记

01-小整数对象池、intern机制

列表在使用的过程中,即使数据相同,但他们依然是各自的一份,不会复用,用id(var)可以查看这个变量的内存地址。

相同内容的列表,他们的地址不同;对于相同小整数内容的对象池,他们的地址相同

python解释器为了能让程序运行的更快,早就把常用的数据,比如1、2、3等,在python解释器运行的开始阶段就创建了,就只等待被使用了。因为开发人员用这个用的多。
除了1、2、3之外,还有很多,这个范围是[-5, 256]

每一个大整数,均创建一个新的对象。([-5, 256]就是小整数对象池)

python中的另一个特殊机制:字符串驻留:因为字符串不可被修改的,默认自动开启对普通字符串开启intern机制,也就是共用同一份,但如果字符串中包含空格等特殊字符,就不会开启。【这些机制在不同系统中有时会不太一样,比如windows和ubuntu中】

当定义两个相同的字符串时,引用计数为0,会开启垃圾回收(python机制:能少用内存就少用内存,所有语言都是这样其实)

02-垃圾回收:引用计数、循环计数

python采用的是引用计数为主,分代收集机制为辅的策略(了解即可)

C语言、C++都是自己要管理维护内存,极其灵活,有好处也带来坏处,经常内存泄漏。例如malloc(100)申请100个字节,使用free()进行释放,如果忘记释放,遗留的内存就是垃圾,这种遗留内存的现象就是内存泄漏。

虽然python更智能,但是有时也会留下一部分清理不了的内存,这就是我们研究gc的原因。

python为了能够知道当前这个对象有多少个变量指向它,会在每个对象中有一个小小的空间,里面存放 引用计数。

比如a = XXX() b=a 这时候那个引用计数变为2

引用计数有优点,也有缺点:
优点:简单,实时性(一旦没有引用,内存就直接被释放了,不用像其他机制等待时机,这样就把垃圾处理的时间均摊了)
缺点:维护引用计数消耗资源;解决不了循环引用的问题

循环引用非常危险(附图)

03-Ruby与Python对比,理解垃圾回收

英文原文(这个文章解释垃圾回收非常好):visualizing garbage collection in ruby and python

ruby在代码执行前,创建成千上百个对象,串在链表上,名曰:可用列表。什么时候用空间,什么时候从上面取。
python是什么时候创建对象,什么时候申请内存,什么时候饿了什么时候做一个馒头

当ruby在指向前面链表元素的变量指向后面的时候,前面的不会自动回收【ruby太懒,攒到没有盘子用的时候才去刷】。python这样的时候,那个位置的引用计数就变成0了,python就给删掉了【python太勤快,一个盘子不用了就去刷一个盘子】

Ruby这种方法叫做“标记-清除”,用0-1来标记这个位置,被称为可用位图,来跟踪对象是否被标记了。如果标记的对象是存活的,那么剩下的未标记的对象就只能是垃圾,通过调整内部指针,将它指向一个新链表的方式,来将垃圾回收对象归位到可用列表中。

python中是引用计数:在每个对象中预留空间来处理引用计数,实现比较难;处理相对较慢,不停的更新着众多的引用数值;

python中怎么避免循环引用:引用计数能够解决大部分问题,但解决不了循环引用,所以采用隔代收集的方法:python会创建一个零代列表,找出列表中每个互相引用的对象。根据规则减掉它的引用计数。
也就是如果没有变量指向这个对象,但仍有引用计数,那么意味着它被循环引用了。把经过减引用计数后还不是0的元素移动到新的链子上,然后把留下来的这些称作第一代(从第0代移动过来的),最多有3代。
(从某种意义上说,这种方法类似ruby的标记回收算法)

什么时候才会触发这个减引用计数呢?有个条件,使用gc的阈值。毕竟不能时时刻刻都在做这件事。
python解释器一直在跟踪着这个阈值。

ubuntu打开top,看程序的占用cpu和内存
如果使用导入gc,然后调用gc.disable(),就关掉了python的内存自动回收,如果有内存泄漏,并且while True,那肯定就崩掉了
美国有一架航天飞机就是因为内存泄漏失事了,特斯拉汽车如果内存泄漏,也会出事

04-解决循环引用的方式:隔代收集

三种情况下会触发垃圾回收:
1、gc模块计数器(申请对象个数-释放对象个数)达到阈值后,自动回收垃圾
2、调用gc.collect(),手动回收垃圾【在没有达到条件时,强制清】
3、程序退出的时候,python解释器来回收垃圾

导致引用计数+1的情况:
1、对象被创建:a=23
2、对象被引用:b=a
3、对象作为参数传入函数:func(a)
4、对象作为一个元素存在容器中,例如list1=[a,a](这里+2)

导致引用计数-1的情况:
1、对象别名被显式销毁,del a
2、对象的别名被赋予新的对象,例如a=24
3、对象离开他的作用域,例如f函数执行完毕时,func函数中的局部变量(全局变量不会)
4、对象所在的容器被销毁,或者从容器中删除对象

import sys.getrefcount(a) 获取引用计数的数量

pycharm对默认的python创建的对象自己有一定的优化,所以在pycharm里面测试的话会和理论上不一样

隔代收集:
把对象分成三条链子,这个链子永远存在,分别叫第0代、第1代、第二代。gc模块中有一个长度为3的列表的计数器,可以通过gc.get_count()获取

第1代根据第0代触发了多少次才触发
第2代根据第1代触发了多少次才触发
可以把第0代想象成秒,第1代想象成分,第2代想象成时

gc模块有一个阈值,可以根据gc.get_threshold(),默认为(700,10,10),也就是:
清理第0代的链子的条件是,垃圾数据达到700个;
清理第1代的链子的条件是,第0代链子清理了10次;
清理第2代的链子的条件是,第1代链子清理了10次;

那么在第三条链子上存在的对象,这样的对象说明是程序中的核心,因为经过无数次的清洗,依然在

怎么获取当前一共多少个垃圾,一共清理了多少次
gc.get_count() —> (223, 7, 4) 说明第0条链子上有223个垃圾,第0条清理过7次,第1条清理过4次

(思考:在运行程序时,可以先用gc.disable()增加计算速度,然后到最后再开启垃圾回收gc.enable()后者gc.collect()调用一次)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值