【转】python里面的“垃圾”是怎么回收的

大家都熟悉C和Java的垃圾回收机制,可以帮助机器很好的去释放内存空间,提升内存的使用效率。那么对于胶水语言Python来说,是怎么样做到垃圾回收的呢?

前言:

对于python来说,一切皆为对象,所有的变量赋值都遵循着对象引用机制。程序在运行的时候,需要在内存中开辟出一块空间,用于存放运行时产生的临时变量;计算完成后,再将结果输出到永久性存储器中。如果数据量过大,内存空间管理不善就很容易出现OOM(out of memory)的情况,俗称爆内存,程序可能被操作系统终止。

而对于服务器,内存管理则显得更为重要,不然很容易引发内存泄露。

这里说的泄露,并不是说我们的内存出了信息安全问题,被恶意程序利用了,而是指程序本身没有设计好,导致程序未能释放已经不再使用的内存。

 

计数引用

因为python里面一切皆对象,所有我们看到的一切变量,实质上是对象的一个指针。

当一个对象不再调用的时候,也就是当这个对象的引用计数(指针数)为0的时候,说明这个对象永不可达,自然也就成为了垃圾,需要被回收。可以简单的理解为没有任何变量再指向它。

import os
import psutil

#显示当前python程序占用的内存大小
def show_memory_info(hint):
    pid = os.getpid()
    p = psutil.Process(pid)

    info = p.memory_full_info()
    memory = info.uss / 1024. / 1024
    print('{} memory used:{}MB'.format(hint,memory))

def func():
    show_memory_info('initial')
    a = [i for i in range(10000000)]
    show_memory_info('after a created')
func()
show_memory_info('finished')

########运行结果#############
initial memory used:4.94140625MB
after a created memory used:197.484375MB
finished memory used:5.2421875MB

从输出结果,我们可以看到在调用函数func()之后,在列表a被创建之后,内存迅速占用到了197M;而在函数运行结束之后,内存则返回正常。这是因为,函数内部声明的列表a是局部变量,在函数返回后,局部变量的引用会被注销掉;此时,列表a所指代对象的引用数为0,python便会执行垃圾回收,因此之前占用的大量内存又回来了。

 

接下来,让我们再来看另外一段代码:

import os
import psutil

def show_memory_info(hint):
    pid = os.getpid()
    p = psutil.Process(pid)
    
    info = p.memory_full_info()
    memory = info.uss / 1024. / 1024
    print('{} memory used:{}MB'.format(hint,memory))

def func():
    show_memory_info('inital')
    global a
    a = [i for i in range(10000000)]
    show_memory_info('after a created!')

func()
show_memory_info('finished!')

################输出结果################
inital memory used:4.8671875MB
after a created! memory used:197.4375MB
finished! memory used:197.4375MB

从这段代码中,我们可以看到 global a表示将a声明为全局变量。那么,即使函数返回后,列表的引用依然存在,对象并没有因为函数调用结束而被回收掉,依然占用着分配给它的大量内存。那么,我们再来往下思考,如果我们把变量返回到主程序中接受,结果会怎么样呢? 再来看一段代码:

def func():
    show_memory_info('initail')
    a = [i for i in range(10000000)]
    show_memory_info('after a created:')
    return a 

a = func()
show_memory_info('finished!')

###################output#################
inital memory used:4.8671875MB
after a created! memory used:197.41015625MB
finished! memory used:197.41015625MB

从输出结果可以看出,但我们选择把局部变量的值引用到主程序的时候,在程序执行结束之后,内存的使用情况和作为全局变量完全一致。说明,这种方法依然会造成内存得不到释放。

那么我们怎么看到变量被引用了多少次呢?让我们来看下面一段代码

import sys

a = []
print(sys.getrefcount(a))

def func(a):
    print(sys.getrefcount(a))

func(a)
print(sys.getrefcount(a))

#############output#############
2
4
2

其中涉及到函数调用的时候,会额为增加两次

1.函数栈

2.函数调用

从这里可以看到python并不需要我们像C语言那样的释放内存,但是python同样给我们提供了手动释放内错的方法:gc.collect()

看下面一段代码:

import sys
import gc
import os
import psutil

def show_memory_info(hint):
    pid = os.getpid()
    p = psutil.Process(pid)

    info = p.memory_full_info()
    memory = info.uss / 1024. / 1024
    print('{} memory used:{}MB'.format(hint,memory))

show_memory_info('initial')
a = [i for i in range(10000000)]

del a
gc.collect()
show_memory_info('finished')
print(a)

###############output ####################
initial memory used:4.94140625MB
finished memory used:4.98828125MB
Traceback (most recent call last):
  File "varible.py", line 20, in <module>
    print(a)
NameError: name 'a' is not defined

截止目前,貌似python的垃圾回收机制非常的简单,只要对象引用次数为0,必定为触发gc,那么引用次数为0是否是触发gc的充要条件呢?

原文链接:https://www.heroyf.club/2019/10/24/python_gc/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值