1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
| //引用管理 python是一种动态类型的,面向对象的语言内存管理方式
对象内存管理是基于对象引用的管理!!!
在python中,引用与对象分离: 一个对象可以有多个引用,而每个对象中都存有指向该对象的引用总数,即引用计数
引用计数可以通过sys包中的getrefcount()来查看,例如: >>> from sys import getrefcount >>> a = [1,2,3] >>> print(getrefcount(a)) 2 >>> b = a >>> print(getrefcount(a)) 3 注意: getrefcount所得到的结果会比预期多1
//对象引用对象 可变对象(列表,字典)等它们都是数据容器对象,可以包含多个对象 容器对象中包含的并不是元素对象本身,而是指向各个元素对象的引用
当一个对象a被另一个对象b引用时,a的引用计数将增加1 例如: >>> from sys import getrefcount >>> a = [1,2,3] >>> print(getrefcount(a)) 2 >>> b = [a,a] >>> print(getrefcount(a)) 4
容器对象的引用会构成很复杂的拓扑结构,可以使用objgraph包来绘制其引用关系 例如: >>> import objgraph >>> >>> x = [1,2,3] >>> y = [x,dict(key=1)] >>> z = [y,(x,y)] >>> >>> objgraph.show_refs([z],filename="ref_top.png") Graph written to /tmp/objgraph-YJf5mO.dot (9 nodes) Image generated as ref_top.png
两个对象可能相互引用,从而构成引用环.即时单个对象,自己引用自己,也能构成引用环 例如: >>> aa = [] >>> bb = [aa] >>> aa.append(bb) >>> objgraph.show_refs([aa],filename="aa_top.png") Graph written to /tmp/objgraph-odsHZ9.dot (2 nodes) Image generated as aa_top.png
>>> cc = [] >>> cc.append(cc) >>> objgraph.show_refs([cc],filename="cc_top.png") Graph written to /tmp/objgraph-Wtvf78.dot (1 nodes) Image generated as cc_top.png
引用环会给垃圾回收机制带来很大的麻烦
使用del关键字删除某个引用,也可以用于删除容器中的元素 例如: >>> from sys import getrefcount >>> a = [1,2,3] >>> b = a >>> print(getrefcount(b)) 3 >>> a = 1 >>> print(getrefcount(b)) 2
//垃圾回收 当python中的对象越来越多时,它们占据越来越大的内存,它会在适当的时候启动垃圾回收,用于清理没用的对象 原理上,当python的某个对象的引用计数为0时,即没用任何引用指向该对象时,该对象就称为要被回收的垃圾了.当垃圾回收启动时,python扫描到这个引用计数为0的对象,就会将它所占据的内存清空.
当python运行时,会记录其中"分配对象"和"取消分配对象"的次数.当两者的差值高于某个阀值时,垃圾回收才会启动 我们可以通过gc模块的get_threshold()方法看到该阀值: >>> import gc >>> print(gc.get_threshold()) (700, 10, 10)
700 垃圾启动的阀值,通过gc的set_threshold()重新设置 10 每10次0代回收,会配合1次1代的垃圾回收,此时清理0,1代 10 每10次1代回收,会配合1次2代的垃圾回收,此时清理0,1,2代
也可以手动回收,通过gc模块的collect()方法
python除了上面的基础回收外,还同时采用了分代回收策略:存活的时间越久,越不可能在后面的程序中变成垃圾. python将所有的对象分为0,1,2三代: 所有的新建对象都是0代对象,当某一代对象经历过垃圾回收,依然存活,那么它就进入下一代对象.垃圾回收启动时,一定会扫描所有的0代对象.如果0代对象经过一定次数垃圾回收,那么就启动对0代和1代的扫描清理.当1代也经历了一定次数的垃圾回收后,就会启动对0,1,2代的扫描,即对所有对象进行扫描
//孤立的引用环 引用环的存在会给上面的垃圾回收机制带来很大的困难 例如: >>> a = [] >>> b = [a] >>> a.append(b) >>> del a >>> del b
由于引用环的存在,这两个对象的引用计数都没有将到0,所以不会被垃圾回收. 为了回收这样的引用环,python会复制每个对象的引用计数,可以标记为gc_ref. 假设,每个对象i,该计数为gc_ref_i.python会遍历所有对象i,对于每个对象i所引用的对象j,将相应的gc_ref_j减1,遍历后的结果gc_ref为0,进行垃圾回收.如果不为0,则对象被保留
|