Python 的内存管理架构(Objects/obmalloc.c):
_____ ______ ______ ________
[ int ] [ dict ] [ list ] ... [ string ] Python core |
+3 | | |
_______________________________ | |
[ Python's object allocator ] | |
+2 | ####### Object memory ####### | |
______________________________________________________________ |
[ Python's raw memory allocator (PyMem_ API) ] |
+1 | | |
__________________________________________________________________
[ Underlying general-purpose allocator (ex: C library malloc) ]
0 | |
0. C语言库函数提供的接口
1. PyMem_*家族,是对 C中的 malloc、realloc和free 简单的封装,提供底层的控制接口。
2. PyObject_* 家族,高级的内存控制接口。
3. 对象类型相关的管理接口
PyMem_*
PyMem_家族:低级的内存分配接口(low-level memory allocation interfaces)
Python 对C中的 malloc、realloc和free 提供了简单的封装:
为什么要这么多次一举:
- 不同的C实现对于malloc(0)产生的结果有会所不同,而PyMem_MALLOC(0)会转成malloc(1).
- 不用的C实现的malloc与free混用会有潜在的问题。python提供封装可以避免这个问题。
- Python提供了宏和函数,但是宏无法避免这个问题,故编写扩展是应避免使用宏
源码:
Include/pymem.h #define PyMem_MALLOC(n) ((size_t)(n) > (size_t)PY_SSIZE_T_MAX ? NULL \ : malloc((n) ? (n) : 1)) #define PyMem_REALLOC(p, n) ((size_t)(n) > (size_t)PY_SSIZE_T_MAX ? NULL \ : realloc((p), (n) ? (n) : 1)) #define PyMem_FREE free Objects/object.c /* Python's malloc wrappers (see pymem.h) */ void * PyMem_Malloc(size_t nbytes) { return PyMem_MALLOC(nbytes); } ...
PyMem_New 和 PyMem_NEW
PyMem_Resize和 PyMem_RESIZE
它们可以感知类型的大小
#define PyMem_New(type, n) \ ( ((size_t)(n) > PY_SSIZE_T_MAX / sizeof(type)) ? NULL : \ ( (type *) PyMem_Malloc((n) * sizeof(type)) ) ) #define PyMem_Resize(p, type, n) \ ( (p) = ((size_t)(n) > PY_SSIZE_T_MAX / sizeof(type)) ? NULL : \ (type *) PyMem_Realloc((p), (n) * sizeof(type)) ) #define PyMem_Del PyMem_Free #define PyMem_DEL PyMem_FREE
PyObject_*
PyObject_*家族,是高级的内存控制接口(high-level object memory interfaces)。
注意
- 不要和PyMem_*家族混用!!
- 除非有特殊的内粗管理要求,否则应该坚持使用PyObject_*
源码
Include/objimpl.h #define PyObject_New(type, typeobj) \ ( (type *) _PyObject_New(typeobj) ) #define PyObject_NewVar(type, typeobj, n) \ ( (type *) _PyObject_NewVar((typeobj), (n)) ) Objects/object.c PyObject * _PyObject_New(PyTypeObject *tp) { PyObject *op; op = (PyObject *) PyObject_MALLOC(_PyObject_SIZE(tp)); if (op == NULL) return PyErr_NoMemory(); return PyObject_INIT(op, tp); } PyVarObject * _PyObject_NewVar(PyTypeObject *tp, Py_ssize_t nitems) { PyVarObject *op; const size_t size = _PyObject_VAR_SIZE(tp, nitems); op = (PyVarObject *) PyObject_MALLOC(size); if (op == NULL) return (PyVarObject *)PyErr_NoMemory(); return PyObject_INIT_VAR(op, tp, nitems); }
它们执行两项操作:
- 分配内存:PyObject_MALLOC
- 部分初始化对象:PyObject_INIT和PyObject_INIT_VAR
初始化没什么好看到,但是这个MALLOC就有点复杂无比了...
PyObject_{Malloc、Free}
这个和PyMem_*中的3个可是大不一样了,复杂的厉害!
void * PyObject_Malloc(size_t nbytes) void * PyObject_Realloc(void *p, size_t nbytes) void PyObject_Free(void *p)
Python程序运行时频繁地需要创建和销毁小对象,为了避免大量的malloc和free操作,Python使用了内存池的技术。
- 一系列的 arena(每个管理256KB) 构成一个内存区域的链表
- 每个 arena 有很多个 pool(每个4KB) 构成
- 每次内存的申请释放将在一个 pool 内进行
单次申请内存块
当申请大小在 1~256 字节之间的内存时,使用内存池(申请0或257字节以上时,将退而使用我们前面提到的PyMem_Malloc)。
每次申请时,实际分配的空间将按照某个字节数对齐,下表中为8字节(比如PyObject_Malloc(20)字节将分配24字节)。
Request in bytes Size of allocated block Size class idx
----------------------------------------------------------------
1-8 8 0
9-16 16 1
17-24 24 2
25-32 32 3
33-40 40 4
... ... ...
241-248 248 30
249-256 256 31
0, 257 and up: routed to the underlying allocator.
这些参数由一些宏进行控制:
#define ALIGNMENT 8 /* must be 2^N */ /* Return the number of bytes in size class I, as a uint. */ #define INDEX2SIZE(I) (((uint)(I) + 1) << ALIGNMENT_SHIFT) #define SMALL_REQUEST_THRESHOLD 256
pool
每次申请的内存块都是需要在 pool 中进行分配,一个pool的大小是 4k。由下列宏进行控制:
#define SYSTEM_PAGE_SIZE (4 * 1024)
#define POOL_SIZE SYSTEM_PAGE_SIZE /* must be 2^N */
每个pool的头部的定义如下:
struct pool_header { union { block *_padding; uint count; } ref; /* number of allocated blocks */ block *freeblock; /* pool's free list head */ struct pool_header *nextpool; /* next pool of this size class */ struct pool_header *prevpool; /* previous pool "" */ uint arenaindex; /* index into arenas of base adr */ uint szidx; /* block size class index */ uint nextoffset; /* bytes to virgin block */ uint maxnextoffset; /* largest valid nextoffset */ };
注意,其中有个成员 szidx,对应前面列表中最后一列的 Size class idx。这也说明一个问题:每个 pool 只能分配固定大小的内存块(比如,只分配16字节的块,或者只分配24字节的块...)。
要能分配前面列表中各种大小的内存块,必须有多个 pool。同一大小的pool分配完毕,也需要新的pool。多个pool依次构成一个链表
arena
多个pool对象使用被称为 arena 的东西进行管理。
struct arena_object { uptr address; block* pool_address; uint nfreepools; uint ntotalpools; struct pool_header* freepools; struct arena_object* nextarena; struct arena_object* prevarena; };
arean控制的内存的大小由下列宏控制:
#define ARENA_SIZE (256 << 10) /* 256KB */
一系列的 arena 构成一个链表。
引用计数与垃圾收集
Python中多数对象的生命周期是通过引用计数来控制的,从而实现了内存的动态管理。
但是引用计数有一个致命的问题:循环引用!
为了打破循环引用,Python引入了垃圾收集技术。

Python和C 各有优势,选择应基于项目需求。1)Python适合快速开发和数据处理,因其简洁语法和动态类型。2)C 适用于高性能和系统编程,因其静态类型和手动内存管理。

选择Python还是C 取决于项目需求:1)如果需要快速开发、数据处理和原型设计,选择Python;2)如果需要高性能、低延迟和接近硬件的控制,选择C 。

通过每天投入2小时的Python学习,可以有效提升编程技能。1.学习新知识:阅读文档或观看教程。2.实践:编写代码和完成练习。3.复习:巩固所学内容。4.项目实践:应用所学于实际项目中。这样的结构化学习计划能帮助你系统掌握Python并实现职业目标。

在两小时内高效学习Python的方法包括:1.回顾基础知识,确保熟悉Python的安装和基本语法;2.理解Python的核心概念,如变量、列表、函数等;3.通过使用示例掌握基本和高级用法;4.学习常见错误与调试技巧;5.应用性能优化与最佳实践,如使用列表推导式和遵循PEP8风格指南。

Python适合初学者和数据科学,C 适用于系统编程和游戏开发。1.Python简洁易用,适用于数据科学和Web开发。2.C 提供高性能和控制力,适用于游戏开发和系统编程。选择应基于项目需求和个人兴趣。

Python更适合数据科学和快速开发,C 更适合高性能和系统编程。1.Python语法简洁,易于学习,适用于数据处理和科学计算。2.C 语法复杂,但性能优越,常用于游戏开发和系统编程。

每天投入两小时学习Python是可行的。1.学习新知识:用一小时学习新概念,如列表和字典。2.实践和练习:用一小时进行编程练习,如编写小程序。通过合理规划和坚持不懈,你可以在短时间内掌握Python的核心概念。

Python更易学且易用,C 则更强大但复杂。1.Python语法简洁,适合初学者,动态类型和自动内存管理使其易用,但可能导致运行时错误。2.C 提供低级控制和高级特性,适合高性能应用,但学习门槛高,需手动管理内存和类型安全。


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

VSCode Windows 64位 下载
微软推出的免费、功能强大的一款IDE编辑器

DVWA
Damn Vulnerable Web App (DVWA) 是一个PHP/MySQL的Web应用程序,非常容易受到攻击。它的主要目标是成为安全专业人员在合法环境中测试自己的技能和工具的辅助工具,帮助Web开发人员更好地理解保护Web应用程序的过程,并帮助教师/学生在课堂环境中教授/学习Web应用程序安全。DVWA的目标是通过简单直接的界面练习一些最常见的Web漏洞,难度各不相同。请注意,该软件中

PhpStorm Mac 版本
最新(2018.2.1 )专业的PHP集成开发工具

SublimeText3 英文版
推荐:为Win版本,支持代码提示!

Atom编辑器mac版下载
最流行的的开源编辑器