面试题答案
一键面试引用计数工作原理
- 基本概念:在Python中,每个对象都维护着一个引用计数。引用计数表示指向该对象的引用(变量)的数量。
- 计数变化:
- 增加计数:当创建一个新对象并将其赋值给一个变量,或者将对象作为参数传递给函数,又或者将对象添加到容器(如列表、字典)中时,该对象的引用计数会增加。例如:
a = [1, 2, 3] # 创建列表对象并赋值给a,列表对象引用计数为1
b = a # 将a赋值给b,列表对象引用计数增加到2
- **减少计数**:当变量被删除(使用`del`语句),或者变量被重新赋值指向其他对象,又或者对象从容器中移除时,对象的引用计数会减少。例如:
del a # a指向的列表对象引用计数减1
b = [4, 5, 6] # b重新赋值,原列表对象引用计数再减1
- 垃圾回收:当对象的引用计数变为0时,Python解释器会立即回收该对象所占用的内存空间。
常见编程场景及作用
- 函数参数传递:在函数调用时,实参传递给形参,对象的引用计数增加。函数结束后,形参变量作用域结束,引用计数减少。这确保了函数内使用的对象在函数结束后,如果没有其他引用,会被正确回收。例如:
def func(lst):
pass
my_list = [1, 2, 3]
func(my_list) # my_list引用计数增加,函数结束后引用计数减少
- 容器操作:向容器(如列表、字典)中添加或移除对象时,引用计数相应变化。这有助于管理容器中对象的生命周期。例如:
my_dict = {}
obj = "test"
my_dict['key'] = obj # obj引用计数增加
del my_dict['key'] # obj引用计数减少
可能出现的问题
- 循环引用:当两个或多个对象相互引用时,它们的引用计数永远不会变为0,导致内存泄漏。例如:
class A:
def __init__(self):
self.b = None
class B:
def __init__(self):
self.a = None
a = A()
b = B()
a.b = b
b.a = a
del a
del b
在上述代码中,A
和B
实例相互引用,即使删除了a
和b
变量,这两个实例的引用计数也不会变为0,它们占用的内存无法被回收。为了解决循环引用问题,Python还使用了标记 - 清除和分代回收机制作为辅助垃圾回收手段。