本文来源公众号“python”,仅用于学术分享,侵权删,干货满满。
Python作为一种高级编程语言,为开发者提供了自动内存管理机制,使开发者能够专注于业务逻辑而无需手动管理内存分配和释放。Python的内存管理系统主要由内存分配器和垃圾回收器两部分组成,它们共同协作,确保程序内存使用的高效性和安全性。
Python的内存管理模型采用了私有堆空间的概念,所有Python对象和数据结构都存储在这个私有堆中。解释器负责管理这个堆,程序员无法直接访问它。Python内存管理器定期执行内存分配和释放操作,并具有内置的垃圾回收机制,用于回收不再使用的内存空间。
Python对象的创建过程
当创建一个对象时,Python解释器会调用相应的内存分配函数为对象分配足够的内存空间。Python的内存分配策略采用了一种特殊的机制,称为"内存池",用于提高小对象的分配效率。对于小于512字节的对象,Python会使用内存池进行管理;而对于更大的对象,则直接使用系统的malloc函数进行分配。
# 创建小对象,使用内存池
small_list = [1, 2, 3]
# 创建大对象,直接使用malloc
large_list = list(range(10000))
对象的初始化涉及到类型信息设置和属性初始化。每个Python对象都包含一个指向其类型对象的指针,类型对象中定义了该类型对象的行为和属性。在初始化阶段,对象的引用计数会被设置为1,表示当前有一个引用指向该对象。
对于用户定义的类,对象创建还会触发__new__
和__init__
方法的调用。__new__
方法负责创建对象实例,而__init__
方法则负责初始化对象的属性。
class MyClass:
def __new__(cls, *args, **kwargs):
print("Creating a new instance")
instance = super().__new__(cls)
return instance
def __init__(self, value):
print("Initializing the instance")
self.value = value
# 创建对象时会调用__new__和__init__
obj = MyClass(42)
# 输出:
# Creating a new instance
# Initializing the instance
对象创建完成后,Python会将对象的引用返回给变量。此时,对象的引用计数为1,表示有一个变量引用了该对象。
引用计数与内存管理
引用计数是Python内存管理的核心机制之一。每个对象都有一个引用计数器,记录当前有多少个引用指向它。当创建一个对象并将其赋值给一个变量时,该对象的引用计数设为1。每当有新的引用指向该对象时,引用计数增加1;每当一个引用被销毁或重新赋值时,引用计数减少1。
Python提供了sys.getrefcount
函数,可以查看对象的当前引用计数。需要注意的是,调用这个函数本身会创建一个临时引用,因此返回的值通常比实际引用计数大1。
import sys
# 创建一个对象
x = "Hello, Python"
# 查看引用计数
print(sys.getrefcount(x)) # 输出值比实际引用计数大1
# 创建另一个引用
y = x
# 引用计数增加
print(sys.getrefcount(x))
# 删除一个引用
del y
# 引用计数减少
print(sys.getrefcount(x))
# 输出结果可能类似于:
# 2
# 3
# 2
当对象的引用计数降为0时,Python的垃圾回收器会自动回收该对象占用的内存空间。在回收之前,如果对象定义了__del__
方法,该方法会被调用,允许对象执行一些清理操作。
class ResourceManager:
def __init__(self, resource_id):
self.resource_id = resource_id
print(f"Resource {resource_id} allocated")
def __del__(self):
print(f"Resource {self.resource_id} released")
# 创建对象
rm = ResourceManager(123)
# 删除引用,触发__del__方法
del rm
# 输出:
# Resource 123 allocated
# Resource 123 released
引用计数机制简单高效,但存在一个重要的局限性:无法处理循环引用。循环引用是指两个或多个对象互相引用,形成一个引用环。在这种情况下,即使这些对象都不再被程序的其他部分引用,它们的引用计数也不会降为0,因此不会被自动回收。为了解决这个问题,Python引入了循环垃圾回收器。
分代垃圾回收机制
Python的垃圾回收器采用了分代回收的策略,将对象按存活时间分为三代。新创建的对象被放入第0代。当第0代对象经过一次垃圾回收后仍然存活,就会被移至第1代。同理,当第1代对象经过一次垃圾回收后仍然存活,就会被移至第2代。
每一代都有自己的阈值,当该代中的对象数量超过阈值时,就会触发该代的垃圾回收。第0代的阈值最低,回收频率最高;第2代的阈值最高,回收频率最低。这种分代策略基于"弱代假说":大多数对象在创建后很快就会变为垃圾。
Python提供了gc
模块,可以手动控制垃圾回收的行为。通过这个模块,我们可以查看垃圾回收的统计信息、手动触发垃圾回收、调整垃圾回收的阈值,甚至完全禁用自动垃圾回收。
import gc
# 获取当前阈值
print(gc.get_threshold()) # 输出: (700, 10, 10)
# 手动触发垃圾回收
collected = gc.collect()
print(f"Collected {collected} objects.")
# 禁用自动垃圾回收
gc.disable()
# 重新启用自动垃圾回收
gc.enable()
分代垃圾回收的核心功能是检测和回收循环引用的对象。当垃圾回收器运行时,它会检查每个可能形成循环引用的对象,并构建对象之间的引用图。然后,它使用一种称为"标记-清除"的算法来识别那些无法从程序根对象访问到的对象,并将其回收。
对象内存布局与优化
Python对象在内存中的布局对理解内存使用和优化性能至关重要。每个Python对象都有一个标准的头部结构,包含引用计数和类型指针。这个头部结构通常占用16字节(在64位系统上),然后是对象的具体数据。
Python实现了一些内存优化技术,如小整数对象池和字符串驻留(interning)。这些技术通过重用常见对象来减少内存消耗和提高性能。
# 小整数对象池示例
a = 42
b = 42
print(a is b) # 输出: True(a和b引用同一个对象)
# 大整数不在对象池中
c = 1000000
d = 1000000
print(c is d) # 输出: False(c和d引用不同对象)
# 字符串驻留示例
s1 = "python"
s2 = "python"
print(s1 is s2) # 输出: True(s1和s2引用同一个对象)
# 非标识符的字符串可能不会被驻留
s3 = "hello world!"
s4 = "hello world!"
print(s3 is s4) # 结果可能因Python版本和实现而异
对于可变对象(如列表、字典)和不可变对象(如整数、字符串、元组),Python的内存管理策略有所不同。不可变对象可以安全地共享,而可变对象则需要为每次创建分配新的内存空间。
对象销毁与资源回收
当对象不再被程序引用时,它会被标记为可回收,垃圾回收器会在适当的时候回收它占用的内存空间。对象销毁的过程涉及到引用计数降为零或被循环垃圾回收器识别为垃圾。
在对象被销毁之前,如果它定义了__del__
方法,该方法会被调用。__del__
方法通常用于执行一些清理操作,如关闭文件、释放资源等。
class FileHandler:
def __init__(self, filename):
self.file = open(filename, 'w')
print(f"File {filename} opened")
def write(self, data):
self.file.write(data)
def __del__(self):
self.file.close()
print("File closed")
# 创建对象
handler = FileHandler("example.txt")
handler.write("Hello, World!")
# 当handler不再被引用时,__del__方法会被调用
del handler # 输出: File closed
Python也提供了上下文管理器(with语句)作为一种更可靠的资源管理方式。上下文管理器通过__enter__
和__exit__
方法定义资源的获取和释放,确保资源能够得到及时、正确的释放。
class FileHandler:
def __init__(self, filename):
self.filename = filename
self.file = None
def __enter__(self):
self.file = open(self.filename, 'w')
print(f"File {self.filename} opened")
return self
def write(self, data):
self.file.write(data)
def __exit__(self, exc_type, exc_val, exc_tb):
self.file.close()
print("File closed")
returnFalse# 允许异常传播
# 使用上下文管理器
with FileHandler("example.txt") as handler:
handler.write("Hello, World!")
# 文件会在退出with块时自动关闭
总结
Python的内存模型围绕对象的生命周期展开,包括对象的创建、引用管理和销毁等关键环节。理解这个模型有助于编写更高效、更可靠的Python代码。引用计数是Python内存管理的基础,每个对象都有一个引用计数器记录指向它的引用数量。当引用计数降为零时,对象通常会被销毁。为了处理循环引用问题,Python引入了分代垃圾回收机制,根据对象存活时间的长短对其进行分类并采用不同的回收策略。Python还实现了一些内存优化技术,如内存池、小整数对象池和字符串驻留,以提高内存使用效率。对于资源管理,Python提供了__del__
方法和上下文管理器等机制,确保资源能够得到及时、正确的释放。
THE END !
文章结束,感谢阅读。您的点赞,收藏,评论是我继续更新的动力。大家有推荐的公众号可以评论区留言,共同学习,一起进步。