Chinaunix首页 | 论坛 | 博客
  • 博客访问: 308932
  • 博文数量: 21
  • 博客积分: 250
  • 博客等级: 二等列兵
  • 技术积分: 484
  • 用 户 组: 普通用户
  • 注册时间: 2010-10-06 23:10
个人简介

程序猿

文章分类

全部博文(21)

文章存档

2016年(17)

2014年(3)

2013年(1)

分类: Python/Ruby

2016-01-14 22:02:04

整形、字符对象的内存使用


python的内存使用方式和C语言不一样。先分别看一下两种语言创建整形数据时的情况。

C语言

  1. int a = 11111;//给a分配一块int大小的内存,内存中的值为1
  2. a = 22222;//a内存中的值变为2
python
  1. a = 11111 #创建一个值为11111的对象,符号a引用该对象
  2. a = 22222 #创建一个值为22222的对象,a引用该对象,如果对象11111没有被其他符号引用,则被释放。

在python中,可以使用内置函数id()返回对象的内存地址。
  1. >>> a = 11111
  2. >>> id(a)
  3. 139871089948560
  4. >>> b = 11111
  5. >>> id(b)
  6. 139871089948816
可见,尽管a和b赋的值一样,但分别创建了一个对象。

又如:
  1. >>> c = 1
  2. >>> id(c)
  3. 10105824
  4. >>> d = 1
  5. >>> id(d)
  6. 10105824
我们发现,c和d引用的对象地址是一样的。这是因为,python中对短小的整数和字符作了缓存,不需要每次有新符号引用时都创建一个。

使用关键词is可以查看两个符号引用的是否是同一个对象。
  1. >>> a is b
  2. False
  3. >>> c is d
  4. True

容器对象的内存使用


再来看下python中容器的内存使用。python的容器包括list, tuple, dict,python不会对容器作缓存,所以每次创建容器对象地址都不一样。
  1. >>> a = []
  2. >>> id(a)
  3. 139871089318984
  4. >>> b = []
  5. >>> id(b)
  6. 139871089319624
容器中可以包含多个对象的引用。在使用时需要特别注意对象之间的复制问题。
例如:
  1. >>> a = [1,2,3]
  2. >>> b = a
  3. >>> a[0] = 4
  4. >>> b
  5. [4, 2, 3]
执行完b = a这句后,a和b都引用了同一个对象,所以a中对象的任何操作都会影响到b
要使a和b互不影响,需要使用标准库中的copy模块。 
  1. >>> a = [1,2,3]
  2. >>> import copy
  3. >>> b = copy.copy(a)
  4. >>> a[0] = 4
  5. >>> b
  6. [1, 2, 3]
b = copy.copy(a)这句执行后,a和b引用的对象是不同的,所以修改a[0]时不会影响b

但是,copy只是浅拷贝,这么做可能还是会引起问题。看下面的例子:
  1. >>> a = [[1,2], 10, 11]
  2. >>> b = copy.copy(a)
  3. >>> a[0][0] = 9
  4. >>> b
  5. [[9, 2], 10, 11]
虽然a和b引用的是不同对象,但a[0]和b[0]引用了相同的对象,所以a[0]容器中引用的对象被修改时,会影响到b[0]
对于这种情况,我们应该使用深拷贝:
  1. >>> a = [[1,2], 10, 11]
  2. >>> b = copy.deepcopy(a)
  3. >>> a[0][0] = 9
  4. >>> b
  5. [[1, 2], 10, 11]

Python的内存释放


了解了python中对象内存分配,再来了解下python是如何释放无用内存的。

python为每个内存对象维护一个引用计数。当内存对象被其他对象引用时,该对象的计数值加1;当该内存对象的引用被移除时,计数值减1。当引用计数为0时,该对象被释放。这种内存管理方式称为引用计数(reference counting)方式。

我们可以使用sys包中的getrefcount()来查看某个对象的引用计数。注意,getrefcount()函数返回的结果会比实际引用数多1,因为当把引用传入时实际上创建了一个临时的引用。

来看些例子:
  1. >>> import sys
  2. >>> a = 'hello world'
  3. >>> sys.getrefcount(a)
  4. 2
  5. >>> b = a
  6. >>> sys.getrefcount(a)
  7. 3
  8. >>> del(a)
  9. >>> sys.getrefcount(b)
  10. 2
例子中,首先创建了一个'hello world'的对象,符号a引用该对象,此时引用值为1,由于getrefcount函数的问题,返回的值会多1.然后符号b也引用了a的对象,此时引用值为2.最后调用del函数删除了符号a,'hello world'的引用计数值变为1.

python的引用计数是十分高效的。但是,引用计数有一个很大的问题 ,它无法处理循环引用(reference cycle)。
最简单的循环引用如下:
  1. >>> a=[]
  2. >>> a.append(a)
  3. >>> import sys
  4. >>> sys.getrefcount(a)
  5. 3
  6. >>> a is a[0]
  7. True
此时a的对象有2个引用,当调用del(a)删除a时,对象计数值变为1,对象不会被释放,但是已经没有任何途径可以访问这个对象了,这就造成了内存泄露。为了解决这个问题,python引入了垃圾回收(garbage collection)。

关于垃圾回收且听下回分解~

阅读(2588) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~