PythonPython必知必会系列

python必知必会12

2022-03-22  本文已影响0人  Nefelibatas

Python 的内存机制

内存机制主要包括垃圾收集和内存管理两部分。Python 主要使用基于引用计数的垃圾回收机制,用于回收一些不再被使用的内存,以及内存池,用于管理小块内存的申请和释放。

垃圾检测:引用计数

当给一个对象分配一个新名称或者将一个对象放入一个容器(列表、元组或字典)时,该对象的引用计数都会增加。

当对象的引用计数归零后,垃圾回收程序会将对象销毁。

引用计数怎样会减少呢?对一个变量重新赋值,即那个名称标签指向了另一个对象,从而原先的对象的引用计数减少一次。我们还可以使用del语句。注意:del语句并不会销毁对象,而是删除名称而已,也就是说,那个名称所指的对象的引用次数减少了一次。

有了垃圾回收机制,我们就无需手动去分配内存后再手动销毁内存,这一切都由 Python 自己去检测去回收那些不再被使用的内存。

Python 如何利用引用计数去垃圾回收呢?比如,当空列表对象的引用计数变为 0 了,那么该对象就会被当作垃圾回收。引用计数的特点就是高效和易于实现。但是,维护引用计数也是需要消耗资源,我们需要为每个对象保存引用计数。另外,每一次赋值我们就需要重新计算引用计数,这里面就会有另一个问题,比如一个变量 a 引用了另一个变量 b,而 b 又引用了 a,那么他俩的引用计数就永远不会归零,这个就是循环引用的问题。对于这些缺点,Python 又引入了“标记-清除”算法和“分代回收”的垃圾回收机制,它们用来解决以上两个问题,这里就不一一详述了。Python 还是主要使用引用计数,以“标记-清除的回收机制”和“分代回收”为辅的机制。

垃圾回收时,Python 不能进行其它的任务,频繁的垃圾回收将大大降低 Python 的工作效率。Python 只会在特定条件下,自动启动垃圾回收。当 Python 运行时,会记录其中分配对象(object allocation)和取消分配对象(object deallocation)的次数。当两者的差值高于某个阈值时,垃圾回收才会启动。另外,也可以通过gc.collect()手动回收垃圾。

内存管理

一旦引用计数下降到 0,对象有一个特定的释放功能,称为"释放"内存,以便其他对象可以使用它。这里的“释放”并不是真实地释放给操作系统,而是Python会暂时保留它,并根据需要重用它。在许多情况下,这是一个很好的策略,因为它最大限度地减少了内存分配中涉及的操作系统开销。但是,如果 Python 进程长时间运行,它将占用所需的最大内存量。如果应用程序的峰值内存使用量远远大于其平均使用量,则这是浪费,并可能损害整体系统性能以及应用程序本身的性能。

所以,Python 设计了这样的内存管理体系,它由 4 个层次组成:

第0层:操作系统的内存管理接口
第1层:包装层,用于统一各个操作系统的运行行为,保证了兼容性
第2层:创建Python对象的接口
第3层:对象缓存池机制

第 0 层是操作系统的一些接口,比如开内存(malloc)和释放内存(free),确保专用堆中有足够的空间来存储所有与 Python 相关的数据;第 1 层和第 2 层是内存池,有 Python 的接口函数PyMem_Malloc函数实现,当对象小于 256K 时,该层直接分配内存;第 3 层是最上层,也就是我们对 Python 对象的直接操作。而真正的垃圾回收就是在第 2 层中实现的。

在 C 中,如果频繁的调用 malloc 与 free 时,是会产生性能问题的。再加上频繁的分配与释放小块的内存会产生很多内存碎片。Python 在这里主要干的工作有:如果请求分配的内存在 1~256 字节之间就使用自己的内存管理系统,否则直接使用 malloc。这里还是会调用 malloc 分配内存,但每次会分配一块大小为 256k 的大块内存。经由内存池登记的内存到最后还是会回收到内存池。并不会调用 C 的 free 释放掉,这样以便下次使用。

小结

Python 的一切都是对象,对象与引用是分离的。这样就导致了有的拷贝只是拷贝引用,即拷贝和被拷贝的名称都指向同一个对象,而深拷贝则是创建新的副本,即开辟了新空间存放同样大小的一个对象。

Python 的垃圾回收机制使得缩短了开发过程,但懂得了 Python 是如何分配内存以及如何回收,才能够让我们的程序高效使用内存。

上一篇下一篇

猜你喜欢

热点阅读