2018-08-12 python内存管理

python内存管理

1. 引用和对象

  我们先看这样一个赋值语句 a=1

  在 python 中,整数 1 为一个对象,而 a 是一个引用 a→1

  Python是动态类型的语言(动态类型),对象与引用分离。Python通过引用来操作对象。

  在Python中,整数和短小的字符,Python都会缓存这些对象,以便重复使用。

# id()是Python内置的函数,它用于返回对象的身份(identity),也就是内存地址。
# hex 返回一个数的十六进制表示
a = 1
b = 1
print(id(1))
print(id(a))
print(id(b))
print(hex(id(a)))
1644917568
1644917568
1644917568
0x620b7340

  从上边代码的输出可以看出,a 和 b 其实是指向同一个对象的两个引用。

  我们现在使用 is 来检测一下,整数和短小的字符指的是什么。

a = 1
b = 1
print(a is b)
True
a = 'good'
b = 'good'
print(a is b)
True
a = 'good night'
b = 'good night'
print(a is b)
print(a[:4] is 'good')
print(a[:4] is b[:4])
False
False
False
a = [1]
b = [1]
print(a is b)
False

  可以看到,python缓存了整数和短小的字符,所以指向它们的引用都是指向的缓存好的对象。

  对于较长的字符,则是另外分配的内存,即使是相同的字符串的引用,由于它们指向的对象是另外分配的内存,它们的内存地址也是不同的。

a = 'good night'
b = 'good night'
print(hex(id(a)))
print(hex(id(b)))
0x19b922e1070
0x19b922e17b0
a 和 b 指向的内存

  在 Python 中,每个对象都有指向该对象的引用计数。我们可以使用 sys.getrefcount() 来查看某个对象的引用计数,参数传递给 getrefcount()时,会创建一个临时的引用,所以 getrefcount() 会比期望结果多 1。

import sys
a = [1, 2]
sys.getrefcount(a)
2

  Python 中的容器对象,如 list、tuple、dict 等,可以包含多个对象。实际上,它们包含的只是对象的引用而已。

a = [1, 2]
b = [a]
print(b)
[[1, 2]]
a[0] = -1
print(b)
[[-1, 2]]

  使用 del 可以删除一个引用,同时也会减少相应对象的引用计数。此外,将引用指向别的对象也会使引用计数减少。

a = [1]
b = a
c = b
print( sys.getrefcount(c) )
del a # del 删除变量,减少引用计数
print( sys.getrefcount(c) )
b = None # 指向别的对象,减少引用计数
print( sys.getrefcount(c) )
4
3
2

  两个对象还可以相互引用,这样会形成一个引用环。

a = [1]
b = [a]
a.append(b)
print(a)
[1, [[...]]]

  一个对象也有可能会形成引用环。

a = [1]
a.append(a) # 如果是 a = [a],则不会形成引用环,此时 a 为 [[1]]
print(a)
[1, [...]]

2.垃圾回收

  当Python中的对象越来越多,它们将占据越来越大的内存,这个时候就需要垃圾回收(Garbage Collection)了。当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()

  另外,引用环会给 GC 带来很大的麻烦。对于上面提到的一个对象引用环的情况,即使删除了引用 a ,list 对象的引用计数也不会为0。


单对象引用环

  Python 采用了分代回收的策略。这一策略的基本假设是,存活时间越久的对象,越不可能在后面的程序中变成垃圾。我们的程序往往会产生大量的对象,许多对象很快产生和消失,但也有一些对象长期被使用。出于信任和效率,对于这样一些“长寿”对象,我们相信它们的用处,所以减少在垃圾回收中扫描它们的频率。

  Python将所有的对象分为0,1,2三代。所有的新建对象都是0代对象。当某一代对象经历过垃圾回收,依然存活,那么它就被归入下一代对象。垃圾回收启动时,一定会扫描所有的0代对象。如果0代经过一定次数垃圾回收,那么就启动对0代和1代的扫描清理。当1代也经历了一定次数的垃圾回收后,那么会启动对0,1,2,即对所有对象进行扫描。

  这两个次数即上面get_threshold()返回的(700, 10, 10)返回的两个10。也就是说,每10次0代垃圾回收,会配合1次1代的垃圾回收;而每10次1代的垃圾回收,才会有1次的2代垃圾回收。

  另外,引用环会给 GC 带来很大的麻烦。

a = [1]
a.append(a) # 如果是 a = [a],则不会形成引用环,此时 a 为 [[1]]
print(a)
[1, [...]]

  对于上面提到的一个对象引用环的情况,即使删除了引用 a ,list 对象的引用计数也不会为0,不会被垃圾回收。


单对象引用环

  为了回收这样的引用环,Python复制每个对象的引用计数,可以记为 gc_ref。假设,每个对象 i,该计数为 gc_ref_i。Python 会遍历所有的对象 i。对于每个对象 i 引用的对象 j,将相应的 gc_ref_j 减 1。在结束遍历后,gc_ref 不为0的对象,和这些对象引用的对象,以及继续更下游引用的对象,需要被保留。而其它的对象则被垃圾回收。


解决引用环

  下面例子中 a 指向的对象的引用计数为 2(=3-1),在去除引用 a 后变为 1,由于对象引用了本身,所以它的引用计数应该减一,即 gc_ref = 0,这表明改对象应该被垃圾回收。

a = [1]
a.append(a) # 如果是 a = [a],则不会形成引用环,此时 a 为 [[1]]
print(a)
print(sys.getrefcount(a))
[1, [...]]
3

参考链接:http://www.cnblogs.com/vamei/p/3232088.html

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容