对象的生命周期

对象的生命周期

程序中的变量必须保存在内存中,而一个变量能够在内存中存在多久,对于编写程序就很重要了。

如果我们想要访问一个变量,但是这个变量已经被销毁掉了,情况就会很尴尬,在c语言中就会出现野指针的问题,程序会直接挂掉。这个时候指针依然指向某个内存地址,但是这个内存地址已经不是给原先的变量使用的了。运气好一点,内存地址还没有分配出去,那么通过指针获得的变量的值还没有变,程序仍然能继续运行。运气不好的话,这段内存被分配给了新的变量,里面的内容已经被改变掉了,程序就不知道跑飞到什么地方去了。不管是上面两种情况中的哪一种,变量的使用都是程序完全无法控制的,这是不应该出现的情况。而且这种问题一旦出现,只能在程序运行时debug,还不好找到。

所以在java或者python这种语言中,一般都会引入生命周期的概念和垃圾回收机制。生命周期是一个抽象的概念,垃圾回收机制则是执行这个思想的具体实体。

生命周期这个概念的核心其实可以用一句话来总结:

一个人真正的死亡是这个世界上最后一个记得你的人将你遗忘。

生命周期说的也是同一件事情。
python中的变量或者对象通过一个叫做引用的东西来找到。引用可以理解为一段内存的别名。一个内存可以有很多个名字。举个例子。

python

lst1 = [1, 2, 3]
lst2 = lst1

python

print(id(lst1))
print(id(lst2))

输出

4507640264
4507640264

在上面这几行代码中,我们让python给我们分配了一段内存用于存储一个列表的对象,然后我们给这个对象起了两个名字,lst1和lst2.那么这个时候我们通过这两个名字都可以操作到这个对象。

python

lst1.append(11)

python

print(lst2)
[1, 2, 3, 11]

python

del lst2[0]

python

lst1

输出

[2, 3, 11]

可以看到,不论我们用那个名字对这个对象进行操作,这段内存都会被改变,从表面上看另一个引用指向的对象也被改变了,但实际上他们指向的是同一段内存。
那么,很自然的,一个对象能在内存中活多久,直接取决于还有没有引用指向它不久好了。一个对象,都叫不上名字,还怎么往程序里面写呢?
python的垃圾回收机制也就是这么做的,它会给每个内存中的对象创建一个引用计数器,比如上面这个列表对象,现在有两个引用指向它,这个对象的引用计数器就是2.接下来,我们把其中的一个引用给删除掉。

python

del lst1

python

lst2

输出

[2, 3, 11]

lst1这个引用被我们删除了,但是我们发现,之前创建的那个列表对象依然存在。现在该对象的引用计数器为1。接下来我们把lst2这个引用也删除掉。

python

del lst2

现在就不用想着从程序角度再去访问这段内存了,因为你已经不知道这个内存的名字了。如果是用C语言这种更接近底层的语言,还可以在知道内存地址的时候直接用指针访问,在python这种高级语言中,为了避免程序员的错误,直接把指针给ban掉了。现在程序员访问不到那个对象,也就是说引用计数器已经为0了,python解释器就会直接把这块内存清理掉,知道需要分配内存的之后再根据需要分配给其他对象。但是虽然可能用到了同一段内存,他们也不是一个对象了。之前的对象已经死掉了。

下面还有两个特殊的例子来分析一下:

  1. 函数中的局部变量生命周期怎么处理??

python

def func(a, b):
    c = 0
    print(a + b)
    print(c)

在这个函数中,我们声明了3个变量。其中变量c是显式声明的,而变量a,b都是隐式声明的。这三个引用变量出了函数func之后,其生命周期都结束了,也就是说,在函数外部并不存在这三个变量的引用,但是引用a和b是用传引用的方式将变量传进来的,也就意味着a,b指向了在其他地方声明的一段内存,这段内存依然可以用原先的引用进行访问,但是出了函数func之后就不能再用a和b对其进行访问了,只有在原先的引用已经被删除之后,这段内存才会被销毁。

python

l1 = [1, 2, 3]
l2 = ['abc', 'def']
func(l1, l2)

输出

[1, 2, 3, 'abc', 'def']
0

python

print(l1)
print(l2)

输出

[1, 2, 3]
['abc', 'def']

如果传入的参数是直接以对象形式传入的,那么在引用a,b死亡的时候,这段内存就会被销毁

python

func([1, 2], [3, 4])

输出

[1, 2, 3, 4]
0
  1. 类成员变量的生命周期

python

class C:
    def __init__(self, a, b):
        self.a = a
        self.b = b
        self.c = 0

在声明类的时候,这个类还没有对象,那么类的成员变量不在内存中,这时候相当于它还没有出生。

如果在初始化类对象的时候,有两种形式.和例1中给函数传对象一样,如果是一个有引用的对象,则在其所有引用都被删除的时候,这个对象会被销毁,如果传的是没有引用的对象,在类对象生命周期结束的时候,其成员变量指向的内存被销毁。

python

var1 = [1, 2, 3]
var2 = [4, 5, 6]
c1 = C(var1, var2)

python

c1.a

输出

[1, 2, 3]

python

c1.b

输出

[4, 5, 6]

python

del var1

python

c1.a

输出

[1, 2, 3]

python

del c1

python

var2

输出

[4, 5, 6]

python

c2 = C(['a'], ['b'])

python

c2.a

输出

['a']

python

c2.b

输出

['b']

python

del c2

总而言之,要搞清楚对象的生命周期问题,就要分清楚对象占有的内存和对象的引用。一旦一段内存没有被引用指着,这段内存就没有存在的意义了,python解释器会将其回收。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值