Python 的内存管理是自动化的,涉及处理一个包含所有 Python 对象和数据结构的私有堆。Python 内存管理器内部确保了此内存的有效分配和释放。本教程将探讨 Python 的内存管理机制,包括垃圾回收、引用计数以及变量是如何存储在栈和堆上的。
内存管理组件
Python 的内存管理组件提供了在整个 Python 程序执行期间对内存资源的有效利用。Python 有三个内存管理组件:
-
私有堆:作为所有 Python 对象和数据的主要存储空间。它由 Python 内存管理器内部管理。
-
原始内存分配器:这一低级组件直接与操作系统交互以预留 Python 私有堆中的内存空间。它确保有足够的空间用于 Python 的数据结构和对象。
-
特定对象的分配器:基于原始内存分配器之上,有几个特定对象的分配器管理不同类型的对象的内存,如整数、字符串、元组和字典。
Python 中的内存分配
Python 以两种主要方式管理内存分配——栈和堆。
栈 - 静态内存分配
静态内存分配是在编译时分配内存并在栈中存储的。这通常是函数调用栈和变量引用的典型情况。栈是一段用于存储局部变量和函数调用信息的内存区域。它按照后进先出(LIFO)的原则运行,在这里,最后添加的项是最先移除的。
栈通常用于基本数据类型变量,如数字、布尔值和字符。这些变量具有固定的内存大小,这是在编译时已知的。
示例
让我们看看一个例子来说明基本类型的变量是如何存储在栈上的。在上面的例子中,名为 x, y 和 z 的变量位于名为 example_function() 的函数内。它们存储在栈上,并且当函数执行完成后,它们会自动从栈上移除。
def my_function():
x = 5
y = True
z = 'Hello'
return x, y, z
print(my_function())
print(x, y, z)
执行上述程序,您将得到以下输出:
(5, True, 'Hello')
Traceback (most recent call last):
File "/home/cg/root/71937/main.py", line 8, in <module>
print(x, y, z)
NameError: name 'x' is not defined
堆 - 动态内存分配
动态内存分配发生在运行时,适用于非基本类型的对象和数据结构。这些对象的实际数据存储在堆上,而对它们的引用则存储在栈上。
示例
让我们观察一个例子,创建一个列表动态地分配堆上的内存。
a = [0]*10
print(a)
执行上述程序,您将得到以下结果:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Python 中的垃圾回收
Python 的垃圾回收是指自动释放不再使用的对象所占用的内存的过程,使其可供其他对象使用。Python 的垃圾收集器在程序执行期间运行,并在对象的引用计数降至零时激活。
引用计数
Python 的主要垃圾收集机制是引用计数。Python 中的每个对象都维护一个引用计数,该计数跟踪有多少别名(或引用)指向它。当一个对象的引用计数降至零时,垃圾收集器就会释放该对象。
引用计数的工作原理如下:
-
增加引用计数:当创建了一个指向对象的新引用时,引用计数增加。
-
减少引用计数:当移除一个指向对象的引用或超出作用域时,引用计数减少。
示例
下面是一个演示 Python 中引用计数工作的例子。
import sys
name = "Tutorialspoint"
print("初始引用计数:", sys.getrefcount(name))
other_name = "Tutorialspoint"
print("赋值后的引用计数:", sys.getrefcount(name))
string_sum = name + ' Python'
print("拼接后的引用计数:", sys.getrefcount(name))
list_of_names = [name, name, name]
print("创建包含 'name' 三次的列表后的引用计数:", sys.getrefcount(name))
del other_name
print("删除 'other_name' 后的引用计数:", sys.getrefcount(name))
del list_of_names
print("删除列表后的引用计数:", sys.getrefcount(name))
执行上述程序,您将得到以下结果:
初始引用计数: 4
赋值后的引用计数: 5
拼接后的引用计数: 5
创建包含 'name' 三次的列表后的引用计数: 8
删除 'other_name' 后的引用计数: 7
删除列表后的引用计数: 4