赞
踩
阅读本文章最好有一些C语言和python语言的基础的读者
Cpython项目目录介绍:
Doc :文档来源
Grammar :计算机可读语言定义
Include :C 头文件
Lib :用 Python 编写的标准库模块
Mac :macOS 支持文件
Misc :杂项文件
Modules :用 C 编写的标准库模块
Objects :核心类型和对象模型
Parser :Python解析器源代码
PC :Windows 为旧版本的 Windows 构建支持文件
PCBuild :Windows 构建支持文件
Programs:“python”可执行文件和其他二进制文件的源代码
Python :CPython解释器源代码
Tools :用于构建或扩展 CPython 的独立工具
m4 : 用于自动配置 makefile 的自定义脚本
git clone git@github.com:python/cpython.git
首先我们得找到int对象的__sizeof__()在cpython中的源代码,如果之前有研究过cpython我们可以快速定位到对应的源代码在longobject.c中
我们分别在debug模式下和release模式下对cpython进行编译运行
debug模式下,变量a大小位40字节
release模式下,变量a大小位24字节,也就是说我们平时用的正式发布版本的(即本机安装的)64 位python,当a=0时占用的大小为24字节
debug和release模式下,为何同一个类型和值相同的变量,为何占用内存大小不一样呢,我们可以从源码入手来分析一波。
从上面的分析我们可以知道__sizeof__()的最终结果为,且很容易知道当a=0时,Py_ABS(Py_SIZE(self))*sizeof(digit)的结果为0,那么a变量的大小即为offsetof(PyLongObject, ob_digit)的大小,宏offsetof(type, member-designator) 结果在一个常数整数 size_t 类型是一个结构成员的结构从一开始的字节偏移,在这里即ob_digit相对于PyLongObject结构体开始的偏移量
res = offsetof(PyLongObject, ob_digit) + Py_ABS(Py_SIZE(self))*sizeof(digit);
回顾一下c库offset
#include <stddef.h> #include <stdio.h> struct address { char name[50]; char street[50]; int phone; }; int main() { printf("address 结构中的 name 偏移 = %d 字节。\n", offsetof(struct address, name)); printf("address 结构中的 street 偏移 = %d 字节。\n", offsetof(struct address, street)); printf("address 结构中的 phone 偏移 = %d 字节。\n", offsetof(struct address, phone)); return 0; }
执行结果如下
所以最终就把问题转移到了PyLongObject上了,那么我们来看看PyLongObject的定义,按住Ctrl把鼠标发到PyLongObject,即可跳转到定义
由4我们可以对int___sizeof___impl增加一些调试信息,如下
static Py_ssize_t
int___sizeof___impl(PyObject *self)
/*[clinic end generated code: output=3303f008eaa6a0a5 input=9b51620c76fc4507]*/
{
Py_ssize_t res; printf("offsetof:%d,PyVarObject:%d,Py_ssize_t:%d,PyObject:%d,PyTypeObject*:%d\n", offsetof(PyLongObject, ob_digit),sizeof(PyVarObject),sizeof(Py_ssize_t),sizeof(PyObject),sizeof(PyTypeObject*));
res = offsetof(PyLongObject, ob_digit) + Py_ABS(Py_SIZE(self))*sizeof(digit);
return res;
}
根据5中的修改用visual studio进行重新编译cpython可以知道release和debug下的结果为,可以知道debug和release下PyObject结构体大小相差了16字节。所以我们现在只需要重点研究PyObject的定义信息。
debug
//offsetof:40,PyVarObject:40,Py_ssize_t:8,PyObject:32,PyTypeObject*:8
release
//offsetof:24,PyVarObject:24,Py_ssize_t:8,PyObject:16,PyTypeObject*:8
从之前的分析可以知道PyObject结果如下,*很明显Py_ssize_t和_typeobject 均为8字节,那么多的16字节来自哪里呢,可以注意到_PyObject_HEAD_EXTRA
typedef struct _object {
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
} PyObject;
跳转到宏_PyObject_HEAD_EXTRA的定义如下
由上图可知当在Py_DEBUG模式(即为我们debug模式)下,会定义宏Py_TRACE_REFS,当定义了弘Py_TRACE_REFS时宏_PyObject_HEAD_EXTRA的具体类容为如下,当宏Py_TRACE_REFS存在时会定义指针变量_ob_next和_ob_prev
#ifdef Py_TRACE_REFS
/* Define pointers to support a doubly-linked list of all live heap objects. */
#define _PyObject_HEAD_EXTRA \
struct _object *_ob_next; \
struct _object *_ob_prev;
#define _PyObject_EXTRA_INIT 0, 0,
#else
#define _PyObject_HEAD_EXTRA
#define _PyObject_EXTRA_INIT
#endif
所以多的16字节的出处就很明显了,即指针_ob_next和_ob_prev,这两个指针主要作用是在debug模式下用于追踪所有活动堆对象的双向链表。这两个指针的详细用途后面我们再来慢慢研究,今天的分析就到这里
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。