垃圾回收机制
- 在python中创建变量的时候,系统会自动在内存中开辟一个空间,用于存储数据,但是内存是有限的,而创建的变量不一定全部都被使用,但是又占用这内存,如果不加以管理的话,会导致内存溢出,程序直接中止。所以python有自己的垃圾回收机制。
引用计数
- 在python中,一个变量被引用一次就记一次数,可以使用sys.getrefcount()方法查看对象引用次数。
- 如下所示
- 一般当变量引用次数为零时,系统会回收该变量。
- 当getrefcount(a)也会算一次引用,但是一个程序多次使用该方法,只会算一次引用。
- 当调用func函数的时候,a被引用了两次,调用一次,传参一次。而当函数运行结束时,a只被引用了两次,说明在函数内的引用被系统自动释放了。 ```c from sys import getrefcount
1
a = [1,2,3]
2
print(getrefcount(a))
4
def func(a): print(getrefcount(a))
3
func(a)
print(getrefcount(a))
<a name="NkaRs"></a>#### 循环引用- 有些特殊情况,当变量的引用次数即使为零,也不一定会被系统主动回收。比如两个变量循环引用。- 可以使用gc.collect()尽行回收,当不主动回收时,内存变化如下```cdef show_memery(s):# 获取当前进程pid = os.getpid()# 获取当前对象p = psutil.Process(pid)# 获取内存信息info = p.memory_full_info()# 内存占用m = info.uss/1024/1024print(f"{s}: 占用{m}MB")def func():a = [i for i in range(1000000)]b = [i for i in range(1000000)]a.append(b)b.append(a)show_memery("created")show_memery("start")func()# gc.collect()show_memery("end")

- gc.collect()主动回收后的结果
weakref
- 弱引用:如果一个类实例化对象除了弱引用,其他实例被销毁,则弱引用对象也会被销毁,可以避免循环引用。 ```python
import weakref import sys
class MyClass(object): def del(self): print(self, “del“)
my_class = MyClass()
弱引用my_class
your_class = weakref.ref(my_class)
查看my_class、your_class地址
print(hex(id(my_class)), hex(id(your_class)))
删除实例对象
del my_class
print(your_class)
<a name="MkXY8"></a>#### <a name="6T6F5"></a>####<a name="2hRk2"></a>#### 单例模式- 当一个类被创建,需要被多个模块调用多次时,可以使用单例模式以节约内存,如下所示,创建的sing1和sing2都是同一个地址,类似于a = b = 1, a和b都是指向同一个地址。- 和弱引用不同的是,单例模式中,删除实例对象对其他的实例并没有影响。```pythonclass MySing(object):__instance = Nonedef __new__(cls):# 如果__instance 为None的话创建一个子类 并反回# 当__instance已经被创建了,就直接返回__instanceif cls.__instance is None:__instance = super().__new__(cls)return __instanceelse:return __instancesing1 = MySing()sing2 = MySing()print(id(sing1), id(sing2))

slots
- 动态绑定
- 如下所示,类的属性使用dict方式打印,即通过字典形式绑定,所以我们可以动态的进行修改。
class People(object):def __init__(self,name,age):self.name = nameself.age = agepeople = People("lulu", 18)people.age = 21print(people.__dict__)

- 使用字典形式查询速度比较快,在创建少量的实例时,不会对内存有太大影响,但是如果实例非常多的话,十分影响占用内存。如下,占用1726kb内存。 ```python import tracemalloc
class People(object):
# __slots__ = "name", "age"def __init__(self, name, age):self.name = nameself.age = age
开始跟踪内存分配
tracemalloc.start()
people = [People(“666”, 1) for i in range(10000)]
当前内存分配快照
snapshot = tracemalloc.take_snapshot()
当前对象统计的监测文件
top = snapshot.statistics(“filename”)
for start in top[:10]: print(start)
- 可以通过使用__slots__关闭动态绑定,如下- 可以使用dir方法查看类的属性,但是已经没有__dict__方法了- __slots__方法不会被子类继承```pythonclass People(object):__slots__ = "name", "age"def __init__(self,name,age):self.name = nameself.age = agepeople = People("lulu", 18)print(dir(people))print(people.__dict__)

- 关闭动态绑定之后占用内存 632kb

深拷贝浅拷贝
- 浅拷贝:引用变量的地址,并不会新创建一个内存空间
- 因为python属于动态语言,所以会尽量节省空间以提升效率,默认使用浅拷贝。如下所示,li和li2指向的是同一个地址。
- 当为li添加加一个元素的时候,li2也也随之添加了一个元素 ```python li = [1,2,3]
li2 = li
print(id(li), id(li2))
li.append(1) print(li2)
- 那么如果想要将li的值复制一份,放入新的地址上,那么就需要使用deepcopy方法,如下- 可见,li和li2的地址不同,并且修改了li,li2的值也不会改变```pythonfrom copy import deepcopyli = [1,2,3]li2 = deepcopy(li)print(id(li), id(li2))li.append(1)print(li2)

