【随笔】【Python】内存管理机制


'***** Python内存管理机制 *****'

 - '引用计数'
   引用计数是对变量引用次数的一种标记手段
   当对象被引用时,我们对其计数增加1,当该对象不被引用时,我们对其计数减去1
   如果计数变成了0,说明该对象没有被引用,此时我们就可以删除该对象
   
 - '垃圾回收'
   python中有三种垃圾回收机制,其中引用计数为主,还有标记清除和分代回收
  
 - '内存池'
   python的内存分为大内存和小内存,分界点是256K
   malloc分配大内存,内存池分配小内存
   python的内存池是一个金字塔模式:最顶层是用户对对象的直接操作,中间是分配小内存,最底层是大内存
  

'***** 万物皆对象 *****'

 - 变量:通过变量指针引用对象,变量指针指向具体对象的内存空间,取对象的值
 - 对象:类型已知,每个对象都包含一个头部信息(头部信息:类型标识符和引用计数器)
 PS:变量名没有类型,类型属于对象,变量引用什么类型的对象,变量就是什么类型的
 
 - python缓存了整数和短字符串,因此每个对象在内存中只存在一份,引用所指对象就是相同的,即使使用赋值语句
   也只是创造了新的引用,而不是对象本身
 - python没有缓存长字符串、列表、元组、字典等,内存中可以存在多个相同的对象,可以使用赋值语句创建出新的
   对象
 - 容器对象(列表、字典等)可以包含多个对象,容器对象中包含的并不是元素对象本身,而是指向各个元素对象的
   引用
 - 

>>> a="good"
>>> b="good"
>>> print(id(a))
31250160
>>> print(id(b))
31250160
>>> c="hello china"
>>> d="hello china"
>>> print(id(c))
34565744
>>> print(id(d))
34579696
>>> e=[]
>>> f=[]
>>> print(id(e))
1790472
>>> print(id(f))
1790984
>>> m=(1,2,3)
>>> n=(1,2,3)
>>> print(id(m))
34409912
>>> print(id(n))
34457864


'''
  一个对象被创建出来的时候,因为被New方法引用了,所以他的引用计数就是1,如果它被引用,就会在引用计数上加1,
如果引用它的对象被删除,那么他的引用计数就会被减1。当引用计数变为0就会被垃圾回收机制回收。
  引用计数的优点就是简单、实时性强,缺点就是维护性高,不能解决如下循环引用的情况:
'''
>>> import sys
>>> a=[1,2]
>>> b=[3,4]
>>> a.append(b)
>>> b.append(a)
>>> print(sys.getrefcount(a))
3
>>> print(sys.getrefcount(b))
3
>>> del a
>>> del b
>>> print(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
>>> print(b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'b' is not defined
>>>
'''
  代码a、b间的引用都为1,而a、b被引用的对象删除后都各自减去1(所以他们各自的引用计数还是1),一直是1不会变化。
这样的情况单单靠引用计数就无法解决了。也就引入了 标记-清除 方法。
  标记清除就是用来解决循环引用的问题,只有容器对象才会出现循环引用,比如列表、字典、类、元组。首先为了追踪容器对象,需要每个容器对象维护两个额外的指针,用来将容器对象组成一个链表,指针分别指向前后两个容器对象,方便插入和删除操作。
'''
情形一:
a=[1,3]
b=[2,4]
a.append(b)
b.append(a)
del a
del b

情形二:
a=[1,3]
b=[2,4]
a.append(b)
b.append(a)
del a
'''
  在标记-清除算法中,有两个链表,一个是root链表,另外一个是unreachable链表。
  对于情景一:
    原来再未执行DEL语句的时候,a,b的引用计数都为2,但是在DEL执行完以后,a,b引用次数互相减1。
    a,b陷入循环引用的圈子中,然后标记-清除算法开始出来做事,找到其中一端a,开始拆这个a,b的引
    用环,去掉以后发现,a,b循环引用变为了0,所以a,b就被处理到unreachable链表中直接被做掉。
  对于情景二:
    简单一看那b去环后引用计数还为1,但是a去环,就为0了。这个时候a已经进入unreachable链表中,
    已经被判为死刑了,但是这个时候,root链表中有b。在root链表中的b会被进行引用检测引用了a,
    如果a被做掉了,那么b就...凉凉了,一审a凉了,二审a无罪,所以a被拉到了root链表中。
  PS:
     之所以要剖成两个链表,是基于这样的一种考虑:现在的unreachable可能存在被root链表中的对象,
     直接或间接引用的对象,这些对象是不能被回收的,一旦在标记的过程中,发现这样的对象,就将其从
     unreachable链表中移到root链表中;当完成标记后,unreachable链表中剩下的所有对象就是名副其
     实的垃圾对象了,接下来的垃圾回收只需限制在unreachable链表中即可。
'''


'''
分代回收:

  了解分代回收,首先要了解一下,GC的阈值。随着你的程序运行,Python解释器保持对新创建的对象,
  以及因为引用计数为零而被释放掉的对象的追踪。从理论上说,创建==释放数量应该是这样子。但是如
  果存在循环引用的话,肯定是创建>释放数量,当创建数与释放数量的差值达到规定的阈值的时候,就需
  要用到分代回收。

垃圾回收=垃圾检测+释放。

  分代回收思想将对象分为三代(generation 0,1,2),0代表幼年对象,1代表青年对象,2代表老年对象。
  根据弱代假说(越年轻的对象越容易死掉,老的对象通常会存活更久。) 新生的对象被放入0代,如果该对
  象在第0代的一次gc垃圾回收中活了下来,那么它就被放到第1代里面(它就升级了)。如果第1代里面的对
  象在第1代的一次gc垃圾回收中活了下来,它就被放到第2代里面。
  
  gc.set_threshold(threshold0[,threshold1[,threshold2]])设置gc每一代垃圾回收所触发的阈值。
  
  从上一次第0代gc后,如果分配对象的个数减去释放对象的个数大于threshold0,那么就会对第0代中的对象进行gc垃圾回收检查。 
  从上一次第1代gc后,如过第0代被gc垃圾回收的次数大于threshold1,那么就会对第1代中的对象进行gc垃圾回收检查。
  同样,从上一次第2代gc后,如过第1代被gc垃圾回收的次数大于threshold2,那么就会对第2代中的对象进行gc垃圾回收检查。
'''

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值