当前位置: 首页 > news >正文

python引用计数实例_Python中的引用计数法

[toc]

引用计数法

增量操作

如果对象的引用数量增加,就在该对象的计数器上进行增量操作。在实际中它是由宏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

元组减量操作如下图示:

b78fb3057bd5303afaec16a3bea32e17.png

终结器

就是我们类里经常写的 __del__

终结器指的是与对象的释放处理挂钩的一个功能。列表和字典等内置对象基本上是不能设置终结器的,能定义终结器的只有用户创建的类。

# 一个终结器

class Foo(object):

def __def__(self): # 定义终结器

print("GKD")

这种情况下,当Foo被释放的时候,就会输出GKD。

那么Foo实例实际上是怎么调用的呢?如下示:

412823834c1ae05eb96daf26e210b6d9.png

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那就必须要进行增量操作对变量进行保护了。

像这样的情况,何时对对象的计数器增量,何时减量,完全可以有编程人员自己判断,如果不能判断则就按照规则来。

相关文章:

  • LoadRunner Vuser接口测试脚本 Post举例
  • java 类的继承_Java:类与继承
  • 浅谈对象的复制拷贝
  • java官方网站下载_java下载 7.0 官方版
  • asp.net的% %特定用法
  • java代码shiro注解_java相关:Shiro集成Spring之注解示例详解
  • Oracle Shared Pool机制之——Latches, Locks, Pins and Mutexes
  • java生成apk工具_用Android SDK Build Tools手动构建APK
  • C++常用数据类型
  • java的继承机制有什么好处_JAVA基础-继承机制
  • java类的三种特性_第10章 Java类的三大特性之一:多态
  • 微信公众平台接口测试账号申请
  • java系统类的使用体验_javamelody使用体验
  • 摆花(codevs 1315)
  • java课设要分小组吗_Java团队课程设计-学生成绩管理
  • [NodeJS] 关于Buffer
  • Android Studio:GIT提交项目到远程仓库
  • angular组件开发
  • django开发-定时任务的使用
  • Java|序列化异常StreamCorruptedException的解决方法
  • JavaScript 事件——“事件类型”中“HTML5事件”的注意要点
  • JSONP原理
  • leetcode378. Kth Smallest Element in a Sorted Matrix
  • Mithril.js 入门介绍
  • niucms就是以城市为分割单位,在上面 小区/乡村/同城论坛+58+团购
  • php ci框架整合银盛支付
  • REST架构的思考
  • Three.js 再探 - 写一个跳一跳极简版游戏
  • 记一次用 NodeJs 实现模拟登录的思路
  • 讲清楚之javascript作用域
  • 前端攻城师
  • 实现菜单下拉伸展折叠效果demo
  • 微信小程序填坑清单
  • Oracle Portal 11g Diagnostics using Remote Diagnostic Agent (RDA) [ID 1059805.
  • 【干货分享】dos命令大全
  • 策略 : 一文教你成为人工智能(AI)领域专家
  • #android不同版本废弃api,新api。
  • #if 1...#endif
  • (JS基础)String 类型
  • (k8s中)docker netty OOM问题记录
  • (四)docker:为mysql和java jar运行环境创建同一网络,容器互联
  • (一)ClickHouse 中的 `MaterializedMySQL` 数据库引擎的使用方法、设置、特性和限制。
  • (一)C语言之入门:使用Visual Studio Community 2022运行hello world
  • (原創) 物件導向與老子思想 (OO)
  • (转)德国人的记事本
  • (转)关于pipe()的详细解析
  • (转)关于如何学好游戏3D引擎编程的一些经验
  • (转)我也是一只IT小小鸟
  • (转载)Linux 多线程条件变量同步
  • 、写入Shellcode到注册表上线
  • .Family_物联网
  • .NET Entity FrameWork 总结 ,在项目中用处个人感觉不大。适合初级用用,不涉及到与数据库通信。
  • .NET Framework杂记
  • .net websocket 获取http登录的用户_如何解密浏览器的登录密码?获取浏览器内用户信息?...
  • @JsonSerialize注解的使用