Python中的引用计数法
引用计数法
增量操作
如果对象的引用数量增加,就在该对象的计数器上进行增量操作。在实际中它是由宏Py_INCREF() 执行的。
#define Py_INCREF(op) (((PyObject*)(op))->ob_refcnt++)
#define Py_XINCREF(op) if ((op) == NULL) ; else Py_INCREF(op)
除了增量操作外,还要执行NULL检查,Py_XINCREF(op)。
计数器溢出的问题
Include/object.h
typedef ssize_t Py_ssize_t;
ssize_t型,在32位环境下是int在64位下是long,它的大小由系统决定。这里定义的计数器它是可以为负数的,那么就有问题了,计数器是有符号整数,他能表达的最大数仅仅是无符号整数的一半。这样不会内存溢出吗?我们之前说对象是4字节对齐的,既然是按4字节对齐,我们就可以得到这样分下来,即使所有对象都指向某一个对象,也是不会溢出的。
那么,负的计数器表达的是什么?在debug中,会存在减数操作过度,和增量操作遗失的情况。负的计数器就是为它而设计的。在debug中,Py NegativeRefcount() 函数会把变为负数的对象信息当成错误信息输出。
减量操作
- 先将计数器减量
- 如果得出0以外的数值就调用_Py_CHECK_REFCNT()。它负责检查引用计数器是否变为负数。
- 如果计数器为0就调用 _Py_Dealloc(),与增量操作相同,这里是减量操作。
- NULL检查扩展的减量操作。
其中成员 tp_dealloc 存着负责释放各个对象的函数指针,比如下面这个释放元组对象的函数指针。
Objects/tupleobject.c
static void tupledealloc(register PyTupleObject *op)
{
register Py_ssize_t i;
register Py_ssize_t len = Py_SIZE(op);
if (len > 0) {
i = len;
/* 将元组内的元素进行减量 */
while (--i >= 0)
Py_XDECREF(op->ob_item[i]);
}
/* 释放元组对象 */
Py_TYPE(op)->tp_free((PyObject *)op);
Py_TRASHCAN_SAFE_END(op) }
- 先对元组进行减量,然后在去释放对象。
- 成员tp_free里存着各个对象的释放处理程序。调用PyObject_GC_Del()
PyObject_GC_Del()
void PyObject_GC_Del(void *op) {
PyGC_Head *g = AS_GC(op);
/* 省略部分:释放前的处理 */
PyObject_FREE(g);
}
这里的 PyObject_FREE(),就是上一节中的 PyObject_Free()函数,这个函数会对对象进行释放。不过我是怎么知道的呢。此处又有宏定义。#define PyObject_FREE PyObject_Free。位于Include/objimpl.h
元组减量操作如下图示:
终结器
就是我们类里经常写的 __del__
终结器指的是与对象的释放处理挂钩的一个功能。列表和字典等内置对象基本上是不能设置终结器的,能定义终结器的只有用户创建的类。
# 一个终结器
class Foo(object):
def __def__(self): # 定义终结器
print("GKD")
这种情况下,当Foo被释放的时候,就会输出GKD。
那么Foo实例实际上是怎么调用的呢?如下示:
Objects/typeobject.c:subtype_dealloc():单独拿出终结器的部分
static void subtype_dealloc(PyObject *self)
{
PyTypeObject *type, *base;
destructor basedealloc;
type = Py_TYPE(self);
if (type->tp_del) {
_PyObject_GC_TRACK(self);
type->tp_del(self);
}
/* 省略 */
}
实例的情况下,变量 tp_del 中保存着执行终结器所需的 slot_tp_del() 函数
Objects/typeobject.c:slot_tp_del()
static void
slot_tp_del(PyObject *self)
{
static PyObject *del_str = NULL;
PyObject *del, *res;
self->ob_refcnt = 1;
/* 如果有__del__就执行它 */
del = lookup_maybe(self, "__del__", &del_str);
if (del != NULL) {
res = PyEval_CallObject(del, NULL);
/* 省略部分:错误检查和后处理等 */
}
if (--self->ob_refcnt == 0)
return; /* 退出函数 */
/* 省略部分:最终化时有引用的情况下的应对处理 */
}
先用lookup_maybe(),取出实例中的__del__,然后使用 PyEval_CallObject()来执行它。
插入计数处理
在python中,正常情况是要对对象的计数器进行增量和减量操作的。但是并不是所有地方都需要这样做。
比如说在python中编写c的扩展模块:当从局部变量引用某个对象,大多数情况下是可以不执行计数处理的,因为从局部来说,我们引用它之后给计数器增量,退出后局部后又要减量。这实际上没有任何意义。不过也可以这样做。
本来计数器的作用是告诉GC这个对象被引用了,不要回收。那如果计数器的值已经是大于0了。我们还需要这样的增量计数器吗?增量之后计数器局部使用完后还是会被减量的。
但是在局部变量的作用域中,如果对象的计数器为0那就必须要进行增量操作对变量进行保护了。
像这样的情况,何时对对象的计数器增量,何时减量,完全可以有编程人员自己判断,如果不能判断则就按照规则来。
Python中的引用计数法的更多相关文章
- Python 对象的引用计数和拷贝
Python 对象的引用计数和拷贝 Python是一种面向对象的语言,包括变量.函数.类.模块等等一切皆对象. 在python中,每个对象有以下三个属性: 1.id,每个对象都有一个唯一的身份标识自己 ...
- python中的引用
作为一个python初学者,今天被一个python列表和词典引用的问题折磨了很久,但其实了解了缘由也很简单,记录在此备忘. 首先背书python中的引用对象问题: 1. python不允许程序员选择采 ...
- 2. 引用计数法(Reference Counting)
1960年,George E. Collins 在论文中发布了引用计数的GC算法. 引用计数法意如了一个概念,那就是"计数器",计数器表示的是对象的人气指数, 也就是有多少程序引用 ...
- JVM探究 面试题 JVM的位置 三种JVM:HotSpot 新生区 Young/ New 养老区 Old 永久区 Perm 堆内存调优GC的算法有哪些?标记清除法,标记压缩,复制算法,引用计数法
JVM探究 面试题: 请你弹弹你对JVM的理解?Java8虚拟机和之前的变化更新? 什么是OOM?什么是栈溢出StackOverFlowError?怎么分析 JVM的常用调优参数有哪些? 内存快照如何 ...
- Python中的引用的使用注意
关于Python中的引用的一些使用注意 在python中,在创建一个对象并给它赋予一个变量时,这个赋予的变量仅仅是一个引用它所代表的对象.也就是说新创建的对象只是指向计算机中储存那个对象的内存. 比如 ...
- swift内存管理中的引用计数
在swift中,每一个对象都有生命周期,当生命周期结束会调用deinit()函数进行释放内存空间. 观察这一段代码: class Person{ var name: String var pet: P ...
- 非常易于理解‘类'与'对象’ 间 属性 引用关系,暨《Python 中的引用和类属性的初步理解》读后感
关键字:名称,名称空间,引用,指针,指针类型的指针(即指向指针的指针) 我读完后的理解总结: 1. 我们知道,python中的变量的赋值操作,变量其实就是一个名称name,赋值就是将name引用到一个 ...
- python中的引用传递,可变对象,不可变对象,list注意点
python中的引用传递 首先必须理解的是,python中一切的传递都是引用(地址),无论是赋值还是函数调用,不存在值传递. 可变对象和不可变对象 python变量保存的是对象的引用,这个引用指向堆内 ...
- Python 中的引用和类属性的初步理解
最近对Python 的对象引用机制稍微研究了一下,留下笔记,以供查阅. 首先有一点是明确的:「Python 中一切皆对象」. 那么,这到底意味着什么呢? 如下代码: #!/usr/bin/env py ...
随机推荐
- Linux基础03
** Linux基本操作常用命令(三) ** Linux的软件包 Linux的软件把分为“源码包”和“二进制包” 源码包:免费,开源 二进制包:系统默认包,即RPM包(上一节我们通过rpm卸载过ope ...
- HTML的常用标签属性及使用时需注意的一些细节
前言 本篇随笔的主要是复习一下常用的一些HTML(Hyper Text Markup Language)标签及其属性,并总结一些使用过程中需要注意的一些细节,本篇提及的常用标签主要有: iframe标 ...
- 阿里巴巴战略投资印度最大支付平台Paytm
腾讯科技讯 9月29日,据路透社报道,阿里巴巴和印度最大移动支付和商务平台Paytm今天发布联合声明,宣布阿里巴巴集团及其旗下金融子公司蚂蚁金服将向Paytm注入新资金.阿里称这是一项“战略性的”投资 ...
- javascript工具--控制台详解
一.显示信息的命令 console.log(); //控制台输入 网页中不会输出 console.info(); //一般信息 console.debug(); //除错信息 console.w ...
- ActiveMQ学习笔记(1)----初识ActiveMQ
1. 什么是ActiveMQ? ActiveMQ是Apache推出的,一款开源的,完全支持JMS1.1和j2ee1.4规范的JMS Provider实现的消息中间件(Message Oriented ...
- Python 计算相似度
#计算相似度 #欧式距离 # npvec1, npvec2 = np.array(det_a), np.array(det_b) # similirity=math.sqrt(((npvec1 - n ...
- 第三方库requests
requests库 # 1.记得安装 第三方 模块 requests # pip install requests import requests url = 'http://www.baidu.co ...
- NOIp2018模拟赛四十四
加量不加价?! 昨晚看时间变成了3.5h以为终于变成了正常难度,结果还是国家集训队作业... A题看起来很神仙,B题看上去很神仙,C题一看就知道很神仙: 结果发现B是假题,放榜后发现A也是假题,C是Y ...
- 51nod 1325 两棵树的问题(最大权闭合子图)
首先如果点权全都为正,就可以直接选所有的点. 活在梦里.. 考虑枚举一个点\(i\),作为我们选择的集合中的一个点. 然后我们把另一个点\(j\)选入集合的时候必须把两棵树中\(i\)和\(j\)路径 ...
- python基础8(装饰器)
1.装饰器本质 装饰器的本质:一个闭包函数 装饰器的功能:在不修改原函数及其调用方式的情况下对原函数功能进行扩展 2.装饰器函数 假设要写一个输出函数执行时间的装饰器 def timer(func): ...