整形、字符对象的内存使用
python的内存使用方式和C语言不一样。先分别看一下两种语言创建整形数据时的情况。
C语言
-
int a = 11111;//给a分配一块int大小的内存,内存中的值为1
-
a = 22222;//a内存中的值变为2
python
-
a = 11111 #创建一个值为11111的对象,符号a引用该对象
-
a = 22222 #创建一个值为22222的对象,a引用该对象,如果对象11111没有被其他符号引用,则被释放。
在python中,可以使用内置函数id()返回对象的内存地址。
-
>>> a = 11111
-
>>> id(a)
-
139871089948560
-
>>> b = 11111
-
>>> id(b)
-
139871089948816
可见,尽管a和b赋的值一样,但分别创建了一个对象。
又如:
-
>>> c = 1
-
>>> id(c)
-
10105824
-
>>> d = 1
-
>>> id(d)
-
10105824
我们发现,c和d引用的对象地址是一样的。这是因为,python中对短小的整数和字符作了缓存,不需要每次有新符号引用时都创建一个。
使用关键词is可以查看两个符号引用的是否是同一个对象。
-
>>> a is b
-
False
-
>>> c is d
-
True
容器对象的内存使用
再来看下python中容器的内存使用。python的容器包括list, tuple, dict,python不会对容器作缓存,所以每次创建容器对象地址都不一样。
-
>>> a = []
-
>>> id(a)
-
139871089318984
-
>>> b = []
-
>>> id(b)
-
139871089319624
容器中可以包含多个对象的引用。在使用时需要特别注意对象之间的复制问题。
例如:
-
>>> a = [1,2,3]
-
>>> b = a
-
>>> a[0] = 4
-
>>> b
-
[4, 2, 3]
执行完b = a这句后,a和b都引用了同一个对象,所以a中对象的任何操作都会影响到b
要使a和b互不影响,需要使用标准库中的copy模块。
-
>>> a = [1,2,3]
-
>>> import copy
-
>>> b = copy.copy(a)
-
>>> a[0] = 4
-
>>> b
-
[1, 2, 3]
b = copy.copy(a)这句执行后,a和b引用的对象是不同的,所以修改a[0]时不会影响b
但是,copy只是浅拷贝,这么做可能还是会引起问题。看下面的例子:
-
>>> a = [[1,2], 10, 11]
-
>>> b = copy.copy(a)
-
>>> a[0][0] = 9
-
>>> b
-
[[9, 2], 10, 11]
虽然a和b引用的是不同对象,但a[0]和b[0]引用了相同的对象,所以a[0]容器中引用的对象被修改时,会影响到b[0]
对于这种情况,我们应该使用深拷贝:
-
>>> a = [[1,2], 10, 11]
-
>>> b = copy.deepcopy(a)
-
>>> a[0][0] = 9
-
>>> b
-
[[1, 2], 10, 11]
Python的内存释放
了解了python中对象内存分配,再来了解下python是如何释放无用内存的。
python为每个内存对象维护一个引用计数。当内存对象被其他对象引用时,该对象的计数值加1;当该内存对象的引用被移除时,计数值减1。当引用计数为0时,该对象被释放。这种内存管理方式称为引用计数(reference counting)方式。
我们可以使用sys包中的getrefcount()来查看某个对象的引用计数。注意,getrefcount()函数返回的结果会比实际引用数多1,因为当把引用传入时实际上创建了一个临时的引用。
来看些例子:
-
>>> import sys
-
>>> a = 'hello world'
-
>>> sys.getrefcount(a)
-
2
-
>>> b = a
-
>>> sys.getrefcount(a)
-
3
-
>>> del(a)
-
>>> sys.getrefcount(b)
-
2
例子中,首先创建了一个'hello world'的对象,符号a引用该对象,此时引用值为1,由于getrefcount函数的问题,返回的值会多1.然后符号b也引用了a的对象,此时引用值为2.最后调用del函数删除了符号a,'hello world'的引用计数值变为1.
python的引用计数是十分高效的。但是,引用计数有一个很大的问题 ,它无法处理循环引用(reference cycle)。
最简单的循环引用如下:
-
>>> a=[]
-
>>> a.append(a)
-
>>> import sys
-
>>> sys.getrefcount(a)
-
3
-
>>> a is a[0]
-
True
此时a的对象有2个引用,当调用del(a)删除a时,对象计数值变为1,对象不会被释放,但是已经没有任何途径可以访问这个对象了,这就造成了内存泄露。为了解决这个问题,python引入了垃圾回收(garbage collection)。
关于垃圾回收且听下回分解~
阅读(2605) | 评论(0) | 转发(0) |