一、关于Python存储的问题

1.Python中万物皆对象,所以Python的存储问题就是对象的存储问题,每一个对象Python都会分配一块内存进行存储。

2.存储多个相同的整数和浮点类型等较小的字符类型时,Python会执行缓存机制,不会同时存储多个相同的对象。

3.列表、元组、字典等复杂数据类型存储其他对象时,存储的是对象的内存地址,而不是对象本身。

二、Python的三种内存管理机制

1.引用计数机制

2.垃圾回收机制

3.内存池机制

三、引用计数机制

定义:使用引用计数器方法,当对象的引用数为0时,释放该对象内存空间的机制。

(1)引用计数器方法:对象每多一个引用,计数加一;每减少一个引用,计数减一。

(2)引用增加的条件:①对象被创建时,计数+1 例:p = person()

                                      ②对象被引用时,计数+1 例:p1 = p

                                      ③对象作为函数的参数时,计数+1 例:person(p)

                                      ④对象作为容器数据类型的元素时,计数+1 例:b=[p]

(3)引用减少的条件:①对象的引用被删除时,计数-1 例:del p1

                                      ②对象的引用被赋予其他对象时,计数-1  例:p1 = 1

                                      ③对象作为参数的函数执行完毕后,计数-1

                                      ④对象作为容器数据类型的元素被删除,或容器被删除时,计数-1

(4)查看引用对象个数的方法:导入sys模块,使用模块中的getrefcount(对象)方法,由于这里也是一个引用,故输出的结果多1

from sys import getrefcount

a = [1, 2, 3]
print(getrefcount(a)) # 2

b = a
print(getrefcount(b)) # 3

四、垃圾回收机制

引用计数机制是一种最直观、最简单的垃圾回收机制,当引用计数为0时会被自动释放内存。但当两个容器对象之间互相引用,而且没有其他外部引用时,两个对象的引用计数都为1,这种引用称为循环引用。循环引用不会被引用计数机制自动清除,需要启动新的垃圾回收机制来处理循环引用。

循环引用示例:

a = []
b = []
a.append(b)
b.append(a)
print(a)  # [[[…]]]
print(b)  # [[[…]]]

找到循环引用并释放内存:

1.收集所有容器对象(循环引用只针对于容器对象,其他对象不会产生循环引用),使用双向链表(可以看作一个集合)对这些对象进行引用;

2.针对每一个容器对象,使用变量gc_refs来记录当前对应的应用个数;

3.对于每个容器对象,找到其正在引用的其他容器对象,并将这个被引用的容器对象引用计数减去1;

4.经过步骤3后,检查所有容器对象的引用计数,若为0,则证明该容器对象是由于循环引用存活下来的,并对其进行销毁

分代回收机制:由于上述释放循环引用内存的方法非常繁琐,需要逐个检查每一个容器对象。为了提高检查清除循环引用垃圾的效率,引用分代回收机制。假设将所有内存空间划分为集合A和集合B,所有新分配的内存都划分进集合A,如果某些循环引用经过n次清除依然存活,则将这些循环引用划分进集合B。在下一次清洗中主要对集合A中的对象进行清洗,而隔一段时间后才对集合B中的对象进行清洗,这个过程中集合A中经过多次清洗的对象会转移进集合B。这样每一次清洗过程中需要检查的对象少了,自然效率就会提高。当然在集合B中也会存在一些循环引用,这些循环引用的清除会由于分代回收机制而滞后。
 

垃圾回收的时机:分为自动启动和手动启动。

自动启动:当新增的对象个数与释放对象的个数差达到某一阈值时,会自动启动垃圾回收机制。

bool python 内存模型 python的内存管理机制_bool python 内存模型

手动启动:使用gc模块中的collect()方法,使得执行这个方法时执行分代回收机制

import objgraph
import gc
import sys
class Person(object):
    pass
class Dog(object):
    pass
p = Person()
d = Dog()
p.pet = d
d.master = p
del p
del d
gc.collect()
print(objgraph.count("Person"))
print(objgraph.count("Dog"))

其中objgraph模块的count()方法是记录当前类产生的实例对象的个数