目录
1.生命周期概念
2.监听对象生命周期
3.内存管理机制
1.存储方面
2.垃圾回收方面
1>引用计数器
2>循环引用
3>垃圾回收
1.生命周期概念
指的是一个对象,从诞生到消亡的过程;
当一个对象被创建时,会在内存中分配相应的内存空间进行存储;
当这个对象不再使用,为了节约内存,就会把这个对象释放
涉及问题:
如何监听一个对象的生命过程?
Python是如何掌控一个对象的生命?
2.监听对象生命周期
__new__方法
当我们创建一个对象时,用于给这个对象分配内存的方法
通过拦截这个方法,可以修改对象的创建过程,比如:单例设计模式
class Person:
def __new__(cls, *args, **kwargs):
print("新建了一个对象,但是被拦截了")
pass
# 新建了一个对象,但是被拦截了
# None
p=Person()
print(p)
__init__方法
初始化方法
当对象被创建时,会自动调用这个方法,会把p实例 传递给self,所以操作self就相当于操作外界的Person实例对象。
__del__方法
当对象被释放时,会自动调用这个方法
想要提前释放,就用del p
class Person:
# def __new__(cls, *args, **kwargs):
# print("新建了一个对象,但是被拦截了")
def __init__(self):
print("初始化方法")
self.name='sz'
def __del__(self):
print("这个对象被释放了")
# 初始化方法
# <__main__.Person object at 0x000001975F6B0A90>
# sz
# 这个对象被释放了
p=Person()
print(p)
print(p.name)
小案例
# Person类,打印一下,当前这个时刻,由Person类产生的实例,有多少个
# 创建一个实例,计数+1,如果删除一个实例,计数-1
class Person:
__PersonCount=0
def __init__(self):
print("计数+1")
Person.__PersonCount+=1
def __del__(self):
print("计数-1")
self.__class__.__PersonCount-=1
@classmethod
def log(cls):
print("当前个数%d"%Person.__PersonCount)
Person.personCount=100
p=Person()
p2=Person()
Person.log()
del p
del p2
Person.log()
# 计数+1
# 计数+1
# 当前个数2
# 计数-1
# 计数-1
# 当前个数0
3.内存管理机制
1.存储方面
1.在Python中万物皆对象,不存在基本数据类型。
0,1.2,True,False,”abc”,这些全都是对象。
2.所有对象,都会在内存中开辟一块空间进行存储,
会根据不同的类型以及内容,开辟不同的空间大小进行存储;
返回该空间的地址给外界接收(称为“引用”),用于后续对这个对象的操作,可通过id()函数获取内存地址(10进制),通过hex()函数可以查看对应的16进制地址。
class Person:
pass
p=Person()
print(p)#<__main__.Person object at 0x0000021F732F2B90>
# print(id(p))#2334099712912
# print(hex(id(p)))#0x21f732f2b90
p2=Person()
#p和p2对象占用两块不同的内存空间
print(id(p),id(p2))#2523417750416 2523417473040
3.对于整数和短小的字符,Pthon会进行缓存;不会创建多个相同对象;

#1,2,3
num1=2
num2=2
#对于常用且不可改变的数据,python为了提高性能,会进行缓存,
# 在外界不论创建多少个相同对象,引用的都是同一个对象
print(id(num1),id(num2))#2120676540688 2120676540688
4.容器对象,存储的其他对象,仅仅是其他对象的引用,并不是其他对象本身;

2.垃圾回收方面
1>引用计数器
概念
一个对象,会记录着自身被引用的个数
每增加一个引用,这个对象的引用计数会自动+1
每减少一个引用,这个对象的引用计数会自动-1


对象被作为参数,传入到一个函数中,引用+2,因为函数对象内部有两个属性 引用着这个对象。
class Person:
pass
p1= Person()#1
print(sys.getrefcount(p1))
def log(obj):
print(sys.getrefcount(obj))
log(p1)#3
for attr in dir(log):
print(attr,getattr(log,attr))
对象作为一个元素,存储在容器中,k=[p1]
class Person:
pass
p1= Person()#1
print(sys.getrefcount(p1))#2
k=[p1]#2
print(sys.getrefcount(p1))#3
对象的别名被赋予新的对象,p1=123

查看引用计数
import sys
sys.getrefcount(对象),注意会大一
class Person:
pass
p1= Person()#1
print(sys.getrefcount(p1))#2
p2=p1#2
print(sys.getrefcount(p1))#3
del p2#1
print(sys.getrefcount(p1))#2
del p1#0
print(sys.getrefcount(p1))#NameError: name 'p1' is not defined
2>循环引用
# 内存管理机制=引用计数器机制+垃圾回收机制;
# 当一个对象,如果被引用+1,删除一个引用-1,0就被自动释放;
# objgraph.count()可以查看,垃圾回收器 跟踪的类的对象个数;
import objgraph
class Person:
pass
class Dog:
pass
p=Person()
d=Dog()
print(objgraph.count("Person"))#1
print(objgraph.count("Dog"))#1
#删除p,d之后,对应的对象被释放掉
del p
del d
print(objgraph.count("Person"))#0
print(objgraph.count("Dog"))#0

3>垃圾回收
引用计数器的补充机制;
主要作用:从经历过“引用计数器机制”仍未被释放的对象中,找到“循环引用”,干掉相关对象;
底层机制(了解&难):
怎样找到“循环引用”?

如何提升查找“循环引用”的性能?

# 垃圾回收
import gc
# 新增的对象个数-消亡的对象个数,达到700时,才会触发垃圾检测
# 当0代检测10次后,才会触发0代和1代检测,当1代检测10次后,会触发0代,1代和2代 检测
print(gc.get_threshold())#(700, 10, 10)
gc.set_threshold(200,5,5)
print(gc.get_threshold())#(200, 5, 5)
垃圾回收机制(掌握&简单)
1.自动回收
触发条件
1》开启垃圾回收机制
gc.enable()开启垃圾回收机制(默认开启)
gc.disable()关闭垃圾回收机制
gc.isenabled()判断是否开启
并且
2》达到了垃圾回收的阈值
gc.get_threshold()获取自动回收阈值
gc.set_threshold(1000,5,5)设置自动回收阈值
# 自动回收
import gc
print(gc.isenabled())#True
# 关闭
gc.disable()
print(gc.isenabled())#False
# 开启
gc.enable()
print(gc.isenabled())#True
# 获取自动回收阈值
print(gc.get_threshold())#(700, 10, 10)
# 设置自动回收阈值
gc.set_threshold(1000,5,5)
2.手动回收
# 手动回收
import objgraph
import gc
# 1.定义了两个类
class Person:
pass
class Dog:
pass
# 2.根据这两个类,创建这两个实例对象
p=Person()
d=Dog()
# 3.让两个实例对象直接互相引用,造成循环引用
p.pet=d
d.master=p
# 4.尝试删除可到达引用之后,测试真实对象是否有被回收
del p
del d
# 5.通过“引用计数机制”无法回收,需要借助“垃圾回收机制”进行回收
gc.collect()
print(objgraph.count("Person"))#0
print(objgraph.count("Dog"))#0
注:
python2.x版本,一旦循环引用里面有一个类实现了__del__方法,就不会自动释放。
import objgraph
import gc
# 1.定义了两个类
class Person:
def __del__(self):
print("Person对象被释放了")
pass
class Dog:
def __del__(self):
print("Dog对象被释放了")
pass
p=Person()
d=Dog()
p.pet=d
d.master=p
del p
del d
gc.collect()
print(objgraph.count("Person"))#1
print(objgraph.count("Dog"))#1
解决这个问题有两种方法,一是预防,二是治疗
1》预防:避免循环引用的产生
2》治疗:手动破坏循环引用p.pet=None
强引用:能触发对象的引用计数+1的就是强引用;
弱引用:通过a能够使用真实对象,但是不会让对象的引用计数+1

# 预防:避免循环引用的产生
import objgraph
import gc
import weakref
class Person:
def __del__(self):
print("Person对象被释放了")
pass
class Dog:
def __del__(self):
print("Dog对象被释放了")
pass
p=Person()
d=Dog()
p.pet=d
# d.master=p
d.master=weakref.ref(p)#弱引用
del p
del d
#相当于没有产生循环引用,所以使用引用计数机制就可以释放掉,不需要这个方法
# gc.collect()
print(objgraph.count("Person"))#0
print(objgraph.count("Dog"))#0

# 治疗:手动破坏循环引用p.pet=None
import objgraph
import gc
import weakref
class Person:
def __del__(self):
print("Person对象被释放了")
pass
class Dog:
def __del__(self):
print("Dog对象被释放了")
pass
p=Person()
d=Dog()
p.pet=d
d.master=p
p.pet=None
del p
del d
# gc.collect()
print(objgraph.count("Person"))#0
print(objgraph.count("Dog"))#0

补充:一对多的引用
# 一对多的引用
import objgraph
import weakref
class Person:
def __del__(self):
print("Person对象被释放了")
pass
class Dog:
def __del__(self):
print("Dog对象被释放了")
pass
p=Person()
d1=Dog()
c1=Dog()
# p.pets={"dog": d1,"cat": c1}
# 一对多的引用,这样一个个写比较麻烦
# p.pets={"dog": weakref.ref(d1),"cat": weakref.ref(c1)}
#用weakref.WeakValueDictionary()方法
weakref.WeakValueDictionary({"dog": d1,"cat": c1})
# p.pet=d
d.master=p
del p
del d
print(objgraph.count("Person"))#0
print(objgraph.count("Dog"))#0














暂无评论内容