欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

python对象的内存使用实例讲解

程序员文章站 2024-01-29 17:41:52
内存管理 对象的内存使用 赋值语句: a=1 整数1是一个对象,而a是一个引用。利用赋值语句,引用a指向对象1。python对象与引用分离!Python像使用&l...

内存管理

对象的内存使用

赋值语句:

a=1

整数1是一个对象,而a是一个引用。利用赋值语句,引用a指向对象1。python对象与引用分离!Python像使用“筷子”那样,通过引用来接触和翻动真正的食物——对象。


python内置函数id()

id()用于返回对象的内存地址!

a=1
print(id(a))
print(hex(id(a)))

可以看出对象1的内存地址为:
1428665408(十进制)
0x5527b440

当我们创建多个等于1的引用时,实际上是让所有这些引用指向同一对象!

a=1
b=1
print(id(a))
print(id(b))

输出:

1428665408
1428665408

可见a和b实际上是指向同一对象的两个引用!


is关键字

为了检验两个引用指向同一个对象,我们可以用is关键字。is用于判断两个引用所指的对象是否相同。

a=1
b=1
print(a is b)

a="good"
b="good"
print(a is b)

a="very good morning"
a="very good morning"
print(a is b)

a=[]
b=[]
print(a is b)

输出:
True
True
False
False

python 能够缓存整数和短字符串,因此每个对象只存一份。
比如,所有整数1的引用都指向同一对象。即使使用赋值语句,也只是创造了新的引用,而不是对象本身。

而对于长字符串和其他对象,可以有多个相同的对象,可以使用赋值语句创建出新的对象。


引用计数getrefcount()

在Python中,每个对象都有存有指向该对象的引用总数,即引用计数(reference count)。

我们可以使用sys包中的getrefcount()。

注意:
当使用某个引用作为参数,传递给getrefcount()时,参数实际上创建了一个临时的引用。因此,getrefcount()所得到的结果,会比期望的多1。

from sys import getrefcount

a=[1,2,3]
print(getrefcount(a))

b=a
print(getrefcount(a))

输出:
2
3


对象引用对象

我们也可以自定义一个对象,并引用其它对象

class from_obj(object):
    def __init__(self,to_obj):
        self.to_obj=to_obj

b=[1,2,3]
a=from_obj(b)
print(id(a.to_obj))
print(id(b))

输出:
2090140984648
2090140984648

可见,a引用了对象b。

当一个对象A被另一个对象B引用时,A的引用计数将增加1。

from sys import getrefcount

a=[1,2,3]
print(getrefcount(a))

b=[a,a]
print(getrefcount(a))

输出:
2
4

由于对象b引用了两次a,a的引用计数增加了2。

引用环

两个对象相互引用,从而构成引用环。

a=[]
b=[a]
a.append(b)
print(getrefcount(a))

输出:
3

即对象[]被引用了2次。

即使是一个对象,只需要自己引用自己,也能构成引用环。

a=[]
a.append(a)
print(getrefcount(a))

输出:
3

引用环会给垃圾回收机制带来很大的麻烦。

引用减少-del关键字

a=[1,2,3]
b=a
print(getrefcount(b))

del a
print(getrefcount(b))

输出:
3
2

如果某个引用指向对象A,当这个引用被重新定向到某个其他对象B时,对象A的引用计数减少:

a=[1,2,3]   #a指向对象[1,2,3]
b=a         #b指向对象[1,2,3]
print(getrefcount(b))

a=2         #a重新指向了2
print(getrefcount(b))   #指向对象[1,2,3]的引用减少了1

输出:
3 #指向[1,2,3]的引用为2
2 #指向[1,2,3]的引用为1

垃圾回收

当Python的某个对象的引用计数降为0时,说明没有任何引用指向该对象,该对象就成为要被回收的垃圾了。比如某个新建对象,它被分配给某个引用,对象的引用计数变为1。如果引用被删除,对象的引用计数为0,那么该对象就可以被垃圾回收。比如下面的表:

a = [1, 2, 3]
del a

del a后,已经没有任何引用指向之前建立的[1, 2, 3]这个表。用户不可能通过任何方式接触或者动用这个对象。这个对象如果继续待在内存里,就成了不健康的脂肪。当垃圾回收启动时,Python扫描到这个引用计数为0的对象,就将它所占据的内存清空。

然而,减肥是个昂贵而费力的事情。垃圾回收时,Python不能进行其它的任务。频繁的垃圾回收将大大降低Python的工作效率。如果内存中的对象不多,就没有必要总启动垃圾回收。所以,Python只会在特定条件下,自动启动垃圾回收。当Python运行时,会记录其中分配对象(object allocation)和取消分配对象(object deallocation)的次数。当两者的差值高于某个阈值时,垃圾回收才会启动。

我们可以通过gc模块的get_threshold()方法,查看该阈值:

import gc
print(gc.get_threshold())

返回(700, 10, 10),后面的两个10是与分代回收相关的阈值,后面可以看到。700即是垃圾回收启动的阈值。可以通过gc中的set_threshold()方法重新设置。

我们也可以手动启动垃圾回收,即使用gc.collect()。