元类和垃圾回收机制(七)
一、元类
1、新式类和旧式类
旧式类:
对于旧式类,类(class)和类型(type)并不完全相同。一个旧式类的实例总是继承自一个名为instance的内置类型。如果obj是旧式类的实例,那么obj.class就表示该类,但type(obj)始终是instance类型。
新式类
新式类统一了类(class)和 类型(type)的概念。如果obj是新式类的实例, type(obj) 则与obj.class相同
注意点:
在python 2中,默认所有类都是旧式类。在python2.2之前,根本不支持新式类。从python2.2开始,可以创建新式类,单必须声明它是新式类。
在python3中,所有类都是新式类。因此,python3可以交换一个引用对象的类型和类。
2、类型(type)和类(class)
在python中,一切都是对象。类也是对象,所以一个类(class)必须有一个类型(type)
class MyClass: pass c = MyClass() print(type(c)) print(type(MyClass))

执行结果:实例的类型是类,类的类型是type
type的类型也是type
元类:python中任何新式类以及python3中的任何类都是type元类的一个实例。函数type实际上是一个元类,type就是python在背后用来创建所有类的元类。
注意区分元类和继承的基类:
type:是元类,所有的类都通过type创建出来
object:顶层的基类,所有类的继承定级父类都是object
3、使用type动态定义类
内置type()函数在传递一个参数时,将返回一个对象的类型。对于新式类,通常与对象的class属性相同
type(name, bases, dict)调用type():
name:指定类的名称,将成为该类的name属性
bases:指定继承类的基类元组,将成为该类的bases属性
dict:指定包含类主体定义的名称空间字典,将成为该类的dict属性
def func1(self): print('这个是self.name:', self.name) dict1 = {'name': 'sinder', 'age': 29, 'func': func1} My = type('MyClass', (object,), dict1) print(My) m1 = My() print(m1.name) print(m1.age) m1.func() print(type(m1)) print(My.__dict__)
运行结果:

二、内存管理
1、对象引用
在python中我们来看一个最简单的赋值语句
a = 10
首先这个10它是一个数值类型的对象,那么这个a呢?他只是一个对10这个对象引用的变量,如果在下面我们再赋值:
b = a
那么此时的打印b,返回的结果也是10这个对象,b也是对10这个变量的引用
变量:通过变量指针引用对象
变量指针指向具体对象的内存空间,取对象的值
对象,类型一致,每个对象都包含一个头部信息(头部信息:类型标识符和引用计数器)
a = 10 此时a这个变量指向10这个对象,在10这个对象中,头部中包含的信息为对象的类型,引用计数为1
b = a 此时b引用的是a这个变量,那么a引用的是10这个对象,此时b实际上也是引用10这个对象,引用计数为2
2、引用计数
每个对象创建之后都有个引用计数,当引用计数为0时,那么垃圾回收机制就会把它销毁,回收内存空间。
引用计数存在一个缺陷:当两个对象出现循坏引用时,这两个变量始终不会被销毁,这样会导致内存泄漏。

3、小整数池和intern机制
小整数池:专门划分一块内存(-5~256)
interrn机制:存储连续字符
4、深浅拷贝
copy():分配内存
浅拷贝:只复制内容(类似创建快捷键),引用的是copy的对象
深拷贝:类似粘贴复制
两个对象互相引用,删除不掉,导致内存泄漏。对此python通过标记-清除处理机制进行处理。
5、垃圾回收机制
python中垃圾回收机制以引用计数为主,标记-清除和分代收集两种机制为辅的策略
引用计数上面已经提过
标记清除:
标记清除(Mark—Sweep)』算法是一种基于追踪回收(tracing GC)技术实现的垃圾回收算法。它分为两个阶段:第一阶段是标记阶段,GC会把所有的『活动对象』打上标记,第二阶段是把那些没有标记的对象『非活动对象』进行回收。那么GC又是如何判断哪些是活动对象哪些是非活动对象的呢?
对象之间通过引用(指针)连在一起,构成一个有向图,对象构成这个有向图的节点,而引用关系构成这个有向图的边。从根对象(root object)出发,沿着有向边遍历对象,可达的(reachable)对象标记为活动对象,不可达的对象就是要被清除的非活动对象。根对象就是全局变量、调用栈、寄存器。 mark-sweepg 在上图中,我们把小黑圈视为全局变量,也就是把它作为root object,从小黑圈出发,对象1可直达,那么它将被标记,对象2、3可间接到达也会被标记,而4和5不可达,那么1、2、3就是活动对象,4和5是非活动对象会被GC回收。
标记清除算法作为Python的辅助垃圾收集技术主要处理的是一些容器对象,比如list、dict、tuple,instance等,因为对于字符串、数值对象是不可能造成循环引用问题。Python使用一个双向链表将这些容器对象组织起来。不过,这种简单粗暴的标记清除算法也有明显的缺点:清除非活动的对象前它必须顺序扫描整个堆内存,哪怕只剩下小部分活动对象也要扫描所有对象。
分代回收:
分代回收机制是对引用计数的一个补充机制。在一个模块里创建了很多变量,有些变量被删除了,python解释器底层如何知道对象有没有被删除,对象的引用计数还剩多少?
python分了三代列表进行管理:新创建的变量或对象全部嫁到第一代列表里面,当第一代列表的数量达到700时,python解释器查询第一代列表的对象的引用计数,引用计数为0的对象进行删除,引用计数不为0的对象移到第二代列表,这是第一代列表就空出来;当第一代列表对象数达到700时再次进行清理,当第一代列表清理了十次时,第二代列表进行清理,同理,python解释器查询第二代列表对象的引用计数,引用计数为0的对象删除,不为0的对象移至第三代列表;第二代列表清理了10次第三代列表清理一次。

gc模块
gc模快有一个自动垃圾回收的阀值,即通过gc.get_threshold函数获取到的长度为3的元组,例如(700,10,10)
每一次计数器的增加,gc模块就会检查增加后的计数是否达到阀值的数目,如果是,就会执行对应的代数的垃圾检查,然后重置计数器
通过gc.set_threshold(500, 10 ,10)可以设置阀值的数目
注意:
如果循环引用中,两个对象都定义了__del__方法,gc模块不会销毁这些不可达对象,因为gc模块不知道应该先调用哪个对象的__del__方法,所以为了安全起见,gc模块会把对象放到gc.garbage中,但是不会销毁对象。

浙公网安备 33010602011771号