返回顶部
首页 > 资讯 > 后端开发 > Python >Python C扩展的引用计数问题分析
  • 299
分享到

Python C扩展的引用计数问题分析

2023-06-19 11:06:09 299人浏览 薄情痞子

Python 官方文档:入门教程 => 点击学习

摘要

这篇文章主要讲解了“python C扩展的引用计数问题分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Python C扩展的引用计数问题分析”吧!Python GC机制对于Python这种

这篇文章主要讲解了“python C扩展的引用计数问题分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Python C扩展的引用计数问题分析”吧!

Python GC机制

对于Python这种高级语言来说,开发者不需要自己管理和维护内存。Python采用了引用计数机制为主,标记-清除和分代收集两种机制为辅的垃圾回收机制。

首先,需要搞清楚变量和对象的关系:

  • 变量:通过变量指针引用对象。变量指针指向具体对象的内存空间,取对象的值。

  • 对象,类型已知,每个对象都包含一个头部信息(头部信息:类型标识符和引用计数器)
    Python C扩展的引用计数问题分析

引用计数

python里每一个东西都是对象,它们的核心就是一个结构体:PyObject,其中ob_refcnt就是引用计数。当一个对象有新的引用时,ob_refcnt就会增加,当引用它的对象被删除,ob_refcnt就会减少。当引用计数为0时,该对象生命就结束了。

typedef struct_object {     int ob_refcnt;     struct_typeobject *ob_type;} PyObject;#define Py_INCREF(op)   ((op)->ob_refcnt++) //增加计数#define Py_DECREF(op) \ //减少计数if (--(op)->ob_refcnt != 0) \        ; \else \        __Py_Dealloc((PyObject *)(op))

可以使用sys.getrefcount()函数获取对象的引用计数,需要注意的是,使用时会比预期的引用次数多1,原因是调用时会针对于查询的对象自动产生一个临时引用。

下面简单展现一下引用计数的变化过程。

  • 一开始创建3个对象,引用计数分别是1。

  • 之后将n1指向了新的对象"JKL",则之前的对象“ABC”的引用计数就变成0了。这时候,Python的垃圾回收器开始工作,将“ABC”释放。

  • 接着,让n2引用n1。“DEF”不再被引用,“JKL”因为被n1、n2同时引用,所以引用计数变成了2。

>>> n1 = "ABC">>> n2 = "DEF">>> n3 = "GHI">>> sys.getrefcount(n1)2>>> sys.getrefcount(n2)2>>> sys.getrefcount(n3)2>>> n1 = "JKL">>> sys.getrefcount(n1)2>>> n2 = n1>>> sys.getrefcount(n1)3>>> sys.getrefcount(n2)3>>> sys.getrefcount(n3)2

优缺点:

优点:实时性好。一旦没有引用,内存就直接释放了。实时性还带来一个好处:处理回收内存的时间分摊到了平时。

缺点:维护引用计数消耗资源;循环引用无法解决。

如下图,典型的循环引用场景。对象除了被变量引用n1、n2外,还被对方的prev或next指针引用,造成了引用计数为2。之后n1、n2设成null之后,引用计数仍然为1,导致对象无法被回收。

Python C扩展的引用计数问题分析

标记-清除、分代收集

Python采用标记-清除策略来解决循环引用的问题。但是该机制会导致应用程序卡住,为了减少程序暂停的时间,又通过“分代回收”(Generational Collection)以空间换时间的方法提高垃圾回收效率。详见Python垃圾回收机制!非常实用

Python C扩展的引用计数

Python提供了GC机制,保证对象不被使用的时候会被释放掉,开发者不需要过多关心内存管理的问题。但是当使用C扩展的时候,就不这么简单了,必须需要理解CPython的引用计数。

当使用C扩展使用Python时,引用计数会随着PyObjects的创建自动加1,但是当释放该PyObjects的时候,我们需要显示的将PyObjects的引用计数减1,否则会出现内存泄漏。

#include "Python.h"void print_hello_world(void) {    PyObject *pObj = NULL;    pObj = PyBytes_FromString("Hello world\n"); PyObject_Print(pLast, stdout, 0);    Py_DECREF(pObj);    }

有亮点尤其需要注意:

  • PyObjects引用计数为0后,不能再访问。类似于C语言free后,不能再访问对象。

  • Py_INCREF、Py_DECREF必须成对出现。类似于C语言malloc、free的关系。

Python有三种引用形式,分别为 “New”, “Stolen” 和“Borrowed” 引用。

New引用

通过Python C api创建出的PyObject,调用者对该PyObject具有完全的所有权。一般Python文档这样体现:

PyObject* PyList_New(int len)   Return value: New reference.       Returns a new list of length len on success, or NULL on failure.

针对于New引用的PyObject,有如下两种选择。否则,就会出现内存泄漏。

  • 使用完成后,调用Py_DECREF将其释放掉。

void MyCode(arguments) {    PyObject *pyo;    ...    pyo = Py_Something(args);    ...    Py_DECREF(pyo);}
  • 将引用通过函数返回值等形式传递给上层调用函数,但是接收者必须负责最终的Py_DECREF调用。

void MyCode(arguments) {    PyObject *pyo;    ...    pyo = Py_Something(args);    ...return pyo;}

使用样例:

static PyObject *subtract_long(long a, long b) {    PyObject *pA, *pB, *r;    pA = PyLong_FromLong(a);        pB = PyLong_FromLong(b);        r = PyNumber_Subtract(pA, pB);  Py_DECREF(pA);                  Py_DECREF(pB);                  return r;                       }// 错误的例子,a、b两个PyObject泄漏。r = PyNumber_Subtract(PyLong_FromLong(a), PyLong_FromLong(b));

Stolen引用

当创建的PyObject传递给其他的容器,例如PyTuple_SetItem、PyList_SetItem。

static PyObject *make_tuple(void) {    PyObject *r;    PyObject *v;    r = PyTuple_New(3);         v = PyLong_FromLong(1L);    v = PyLong_FromLong(2L);    PyTuple_SetItem(r, 1, v);PyTuple_SetItem(r, 2, PyUnicode_FromString("three"));return r; }

但是,需要注意PyDict_SetItem内部会引用计数加一。

Borrowed引用

Python文档中,Borrowed引用的体现:

PyObject* PyTuple_GetItem(PyObject *p, Py_ssize_t pos) Return value: Borrowed reference.

Borrowed 引用的所有者不应该调用 Py_DECREF(),使用Borrowed 引用在函数退出时不会出现内存泄露。。但是不要让一个对象处理未保护的状态Borrowed 引用,如果对象处理未保护状态,它随时可能会被销毁。

例如:从一个 list 获取对象,继续操作它,但并不递增它的引用。PyList_GetItem 会返回一个 borrowed reference ,所以 item 处于未保护状态。一些其他的操作可能会从 list 中将这个对象删除(递减它的引用计数,或者释放它),导致 item 成为一个悬垂指针。

bug(PyObject *list) {    PyObject *item = PyList_GetItem(list, 0);    PyList_SetItem(list, 1, PyInt_FromLong(0L));    PyObject_Print(item, stdout, 0); }no_bug(PyObject *list) {    PyObject *item = PyList_GetItem(list, 0);    Py_INCREF(item); PyList_SetItem(list, 1, PyInt_FromLong(0L));    PyObject_Print(item, stdout, 0);    Py_DECREF(item);}

感谢各位的阅读,以上就是“Python C扩展的引用计数问题分析”的内容了,经过本文的学习后,相信大家对Python C扩展的引用计数问题分析这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

--结束END--

本文标题: Python C扩展的引用计数问题分析

本文链接: https://lsjlt.com/news/295775.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Python C扩展的引用计数问题分析
    这篇文章主要讲解了“Python C扩展的引用计数问题分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Python C扩展的引用计数问题分析”吧!Python GC机制对于Python这种...
    99+
    2023-06-19
  • 如何分析C++函数参数引用问题
    这期内容当中小编将会给大家带来有关如何分析C++函数参数引用问题,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。在C++函数参数中,使用了引用作形参,调用时所对应的实参应该是一个数组名,这里的引用是给数组起...
    99+
    2023-06-17
  • C++详细分析讲解函数参数的扩展
    目录一、函数参数的默认值二、函数占位参数三、小结一、函数参数的默认值 C++ 中可以在函数声明时为参数提供一个默认值当函数调用时没有提供参数的值,则使用默认值参数的默认值必须在函数声...
    99+
    2024-04-02
  • C#下byte数组常用扩展浅析
    本篇内容介绍了“C#下byte数组常用扩展浅析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!C# byte数组常用扩展应用一:转换为十六进制...
    99+
    2023-06-17
  • C++中指针问题和引用问题解析
    C++中指针问题和引用问题解析引言:在C++编程中,指针和引用是两个重要的概念。它们都可以用于间接访问变量,但在使用过程中有一些差异。本文将从定义、语法、使用和特点等方面详细解析指针问题和引用问题,并提供具体的代码示例。一、指针问题解析定义...
    99+
    2023-10-22
    指针 引用 解析
  • Python 模板引擎的注入问题分析
    这几年比较火的一个漏洞就是jinjia2之类的模板引擎的注入,通过注入模板引擎的一些特定的指令格式,比如 {{1+1}} 而返回了 2 得知漏洞存在。实际类似的问题在Python原生字符串中就存在,尤其是P...
    99+
    2022-06-04
    模板 引擎 Python
  • 如何进行MySQL Sharding可扩展设计的分析
    本篇文章为大家展示了如何进行MySQL Sharding可扩展设计的分析,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。MySQL Sharding可扩展设计一.背景...
    99+
    2024-04-02
  • 基于OAS设计可扩展OpenAPI的示例分析
    这篇文章的内容主要围绕基于OAS设计可扩展OpenAPI的示例分析进行讲述,文章内容清晰易懂,条理清晰,非常适合新手学习,值得大家去阅读。感兴趣的朋友可以跟随小编一起阅读吧。希望大家通过这篇文章有所收获!随着互联网行业的兴起,开发模式已逐步...
    99+
    2023-06-03
  • DB2使用resize命令扩容表空间引起rebalance问题的分析
    问题描述: 最近一次表空间扩容,环境是DB2 10.1FP4,Suse 11.3,文件系统使用的是Symantec StorageFoundation,有个表空间共32个容器,每个容器大小都是20G,打算扩...
    99+
    2024-04-02
  • Python的C扩展-应用与陷阱
    Python的C扩展-应用与陷阱 1. 背景 2. Python扩展的用武之地-库测试 (1)动态库的测试 (2)静态库的测试 3 python模块级扩展 4 小结 反馈建议 ...
    99+
    2023-01-31
    陷阱 Python
  • Python与C++的引用实例分析
    这篇文章主要讲解了“Python与C++的引用实例分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Python与C++的引用实例分析”吧!例子,这是我的两段根据先序序列创建二叉树的代码:#...
    99+
    2023-06-27
  • Python与C++引用的示例分析
    本篇文章给大家分享的是有关Python与C++引用的示例分析,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。在用Python写建二叉树的代码的时候遇到了这个问题,原因就是把Pyt...
    99+
    2023-06-02
  • C语言计算分段函数问题
    目录C语言计算分段函数简单分段函数用多路分支与if else 级联的写法一、简单分段函数二、代码三、代码及运行结果C语言计算分段函数 修改后的代码如下: #include<st...
    99+
    2022-11-16
    C语言计算分段函数 计算分段函数 C语言分段函数
  • 怎么解决ThinkPHP5.1版本引入composer vendor扩展包的问题
    这篇文章给大家分享的是有关怎么解决ThinkPHP5.1版本引入composer vendor扩展包的问题的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。下面由thinkphp框架教程栏目给大家介绍解决ThinkP...
    99+
    2023-06-15
  • ES6中对象数值扩展的示例分析
    这篇文章主要介绍了ES6中对象数值扩展的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。对象数值扩展Object.is判断两个值是否完...
    99+
    2024-04-02
  • spring @Conditional的使用与扩展源码分析
    目录@Conditional的使用WindowsConditionLinuxConditionConditional的扩展ConditionalOnBeanConditionalOn...
    99+
    2024-04-02
  • ES6中Array常用扩展的示例分析
    这篇文章主要介绍ES6中Array常用扩展的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!from方法将伪数组转换为数组let obj = { ...
    99+
    2024-04-02
  • Python数组实践中的问题分析
    Python数组实践中的问题分析,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。Python数组有很多的知识需要大家详细的学习。Python数组中没有数组的数据结构,但列表很...
    99+
    2023-06-17
  • Python中复数问题的示例分析
    这篇文章主要介绍Python中复数问题的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!问题关于 Python 的复数类型,以下选项中描述错误的是A复数的虚数部分通过后缀“J”或者“j”来表示B对于复数 z,可...
    99+
    2023-06-15
  • c# Struct的一些问题分析
    目录与类的区别:Struct的理论看过好一些,可是工作上基本没有应用过,Class倒处处都有。难道Struct就没有什么使用价值吗?搜了一下如何在类和结构中做出选择? Ϩ...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作