专栏名称: Python开发者
人生苦短,我用 Python。伯乐在线旗下账号「Python开发者」分享 Python 相关的技术文章、工具资源、精选课程、热点资讯等。
目录
相关文章推荐
Python爱好者社区  ·  刚刚,GPT-4.5震撼上线情商逆天!Ope ... ·  4 天前  
Python爱好者社区  ·  我???这就是信息差吗?F12改成绩,19. ... ·  昨天  
Python爱好者社区  ·  两天私活,5w到手 ·  2 天前  
Python中文社区  ·  34万亿“美债危机”黑天鹅要来了?用数据揭晓 ... ·  3 天前  
Python开发者  ·  DeepSeek 四连炸!梁文峰参与开发 ·  5 天前  
51好读  ›  专栏  ›  Python开发者

Python 垃圾回收机制详解

Python开发者  · 公众号  · Python  · 2016-12-18 23:00

正文

(点击 上方公众号 ,可快速关注)


来源:Xjng

链接:www.cnblogs.com/Xjng/p/5128269.html

一.垃圾回收机制


Python中的垃圾回收是以引用计数为主,分代收集为辅。引用计数的缺陷是循环引用的问题。


在Python中,如果一个对象的引用数为0,Python虚拟机就会回收这个对象的内存。


#encoding=utf-8

__author__ = '[email protected]'

class ClassA () :

def __init__ ( self ) :

print 'object born,id:%s' % str ( hex ( id ( self )))

def __del__ ( self ) :

print 'object del,id:%s' % str ( hex ( id ( self )))

def f1 () :

while True :

c1 = ClassA ()

del c1


执行f1()会循环输出这样的结果,而且进程占用的内存基本不会变动


object born , id : 0x237cf58

object del , id : 0x237cf58


c1=ClassA()会创建一个对象,放在0x237cf58内存中,c1变量指向这个内存,这时候这个内存的引用计数是1


del c1后,c1变量不再指向0x237cf58内存,所以这块内存的引用计数减一,等于0,所以就销毁了这个对象,然后释放内存。


1. 导致引用计数+1的情况

  1. 对象被创建,例如a=23

  2. 对象被引用,例如b=a

  3. 对象被作为参数,传入到一个函数中,例如func(a)

  4. 对象作为一个元素,存储在容器中,例如list1=[a,a]


2. 导致引用计数-1的情况

  1. 对象的别名被显式销毁,例如del a

  2. 对象的别名被赋予新的对象,例如a=24

  3. 一个对象离开它的作用域,例如f函数执行完毕时,func函数中的局部变量(全局变量不会)

  4. 对象所在的容器被销毁,或从容器中删除对象


demo


def func ( c , d ) :

print 'in func function' , sys . getrefcount ( c ) - 1

print 'init' , sys . getrefcount ( 11 ) - 1

a = 11

print 'after a=11' , sys . getrefcount ( 11 ) - 1

b = a

print 'after b=1' , sys . getrefcount ( 11 ) - 1

func ( 11 )

print 'after func(a)' , sys . getrefcount ( 11 ) - 1

list1 = [ a , 12 , 14 ]

print 'after list1=[a,12,14]' , sys . getrefcount ( 11 ) - 1

a = 12

print 'after a=12' , sys . getrefcount ( 11 ) - 1

del a

print 'after del a' , sys . getrefcount ( 11 ) - 1

del b

print 'after del b' , sys . getrefcount ( 11 ) - 1

# list1.pop(0)

# print 'after pop list1',sys.getrefcount(11)-1

del list1

print 'after del list1' , sys . getrefcount ( 11 ) - 1


输出:


init 24

after a = 11 25

after b = 1 26

in func function 28

after func ( a ) 26

after list1 = [ a , 12 , 14 ] 27

after a = 12 26

after del a 26

after del b 25

after del list1 24


问题:为什么调用函数会令引用计数+2


3. 查看一个对象的引用计数


sys.getrefcount(a)可以查看a对象的引用计数,但是比正常计数大1,因为调用函数的时候传入a,这会让a的引用计数+1


二.循环引用导致内存泄露


def f2 () :

while True :

c1 = ClassA ()

c2 = ClassA ()

c1 . t = c2

c2 . t = c1

del c1

del c2


执行f2(),进程占用的内存会不断增大。


object born , id : 0x237cf30

object born , id : 0x237cf58


创建了c1,c2后,0x237cf30(c1对应的内存,记为内存1),0x237cf58(c2对应的内存,记为内存2)这两块内存的引用计数都是1,执行c1.t=c2和c2.t=c1后,这两块内存的引用计数变成2.


在del c1后,内存1的对象的引用计数变为1,由于不是为0,所以内存1的对象不会被销毁,所以内存2的对象的引用数依然是2,在del c2后,同理,内存1的对象,内存2的对象的引用数都是1。


虽然它们两个的对象都是可以被销毁的,但是由于循环引用,导致垃圾回收器都不会回收它们,所以就会导致内存泄露。


三.垃圾回收


deff3 () :

# print gc.collect()

c1 = ClassA ()

c2 = ClassA ()

c1 . t = c2

c2 . t = c1

del c1

del c2

print gc . garbage

print gc . collect ()







请到「今天看啥」查看全文