前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python高阶教程-对象管理与垃圾回收

python高阶教程-对象管理与垃圾回收

作者头像
羽翰尘
修改2019-11-26 16:08:07
6200
修改2019-11-26 16:08:07
举报
文章被收录于专栏:技术向技术向

本文由腾讯云+社区自动同步,原文地址 https://stackoverflow.club/book/senior_python/object_and_garbage/

本篇内容来自原创小册子《python高阶教程》,点击查看目录

问题引入

考虑如下代码,运行后输出为?

代码语言:txt
复制
a = {1:[1,2,3]}
b = a.copy()
a[1][0] = 2
print(a)
print(b)

输出:

代码语言:txt
复制
{1:[2,2,3]}
{1:[2,2,3]}

为什么b的输出不是{1:[1,2,3]}呢? 这要从python的对象管理说起。

基于引用的对象管理

python的对象是基于引用来管理的,每个对象维护一个引用计数器。

比如如下代码:

代码语言:txt
复制
var1 = 'A'
var2 = var1
var3 = 'B'

运行后在内存中的分配如下:

可以看到var1 和 var2 的内容都是对象’A’, 但是两者共享一个对象。

copy模块

我们可使用copy模块中的函数来复制一个复杂对象,主要分为shallow copydeep copy两类

shallow copy 仅复制第一层,不递归复制。切片、copy模块的copy函数

deep copy 对子对象进行递归复制。Copy模块的deepcopy函数

上图是不同的函数复制后的效果,可以看到deep copy函数把字典对象里面嵌套的列表对象也完全复制过来了,但是shallow copy不复制嵌套对象。

垃圾的分代回收

有用的对象叫对象,无用的对象叫垃圾,有垃圾就要有回收机制,在python中垃圾回收是自动进行的。主要有如下规则:

  1. 所有对象分为0、1、2三代
  2. 某一代对象经历回收后依旧存活,归入下一代
  3. 一般上,10次0代回收后,配合1次1代回收;其余类推
  4. 0代回收的启动机制:内存分配数 – 删除数 对于第3条,这个数字是可以查看的,有如下代码
代码语言:txt
复制
import gc
gc.get_threshold()

输出:

代码语言:txt
复制
(700, 10, 10)

元组表示的含义为:计数器达到700时启动0代对象的回收;每经过10次0代对象回收,额外对1代对象进行一次回收;每经过10次1代对象回收,额外对2代对象进行一次回收。

对于第4条,在python中维护一个计数器,用来统计内存分配与回收的个数,同时用来启动0代垃圾回收。有如下代码:

代码语言:txt
复制
import gc 
class A():
    pass
print(gc.get_count())
a = A()
print(gc.get_count())
del a
print(gc.get_count())

输出:

代码语言:txt
复制
(574,8,0)
(575,8,0)
(574,8,0)

可以看到经历了一次内存分配与删除后,输出元组的第一个数字经历了574-575-574的变化。不难看出,第二个数字代表距上一次1代垃圾回收已经过去了8个计数器的值,经过上一次2代垃圾回收已经过去了0个计数器的值。

具体的垃圾回收操作是找到引用计数为0的对象并销毁,但是,如果是如下代码呢?

代码语言:txt
复制
a = []
b = []
a.append(b)
b.append(a)

这里a和b循环引用,内存中对象的引用计数永远不为0,该如何进行垃圾回收呢?

循环引用回收-标记清除

标记清除主要有以下规则:

  1. 通过引用计数的副本寻找root object集合 1.1 从表头出发,碰到引用就将目标对象的引用计数值减1 1.2 不为0的对象移到root object 1.3 root object引用的对象移到root object
  2. 链表1中维护root object集合,成为root链表
  3. 链表2中维护剩下的对象,成为unreachable链表
  4. 对unreachable链表的对象进行垃圾回收

用一个例子来解释

在该例中,共有四个对象,分别是’A’, ‘B’, ‘C’, ‘D’, 引用计数都为1, 但是’A’引用了’C’,

‘B’引用了’D’。

标记清除操作开始。首先,复制对象的引用计数。然后,只要对象间有引用,不管是不是循环引用,被引用对象的计数值都要减1. 如A引用了C,C的引用值减1,B引用了D,D的引用值减1,其余类推。最后,把不为0的对象加入root链表,root列表引用的对象加入root链表,其他加入unreachable链表。在此例中,A首先被加入root链表,被A引用的C也被加入root链表。

标记清除操作结束后,对unreachable链表中的对象进行垃圾回收。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-09-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 问题引入
  • 基于引用的对象管理
  • copy模块
  • 垃圾的分代回收
  • 循环引用回收-标记清除
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档