内存管理¶
与 C/C++ 等编程语言不同,MicroPython 通过支持自动内存管理向开发人员隐藏内存管理细节。自动内存管理是操作系统或应用程序用来自动管理内存分配和释放的技术。这消除了诸如忘记释放分配给对象的内存之类的挑战。自动内存管理还避免了使用已释放内存的关键问题。自动内存管理有多种形式,其中之一是垃圾收集 (GC)。
垃圾收集器通常有两个职责;
在可用内存中分配新对象。
释放未使用的内存。
有许多 GC 算法,但 MicroPython 使用 标记和清除 策略来管理内存。该算法有一个标记阶段,它遍历堆标记所有活动对象,而清除阶段则通过堆回收所有未标记的对象。
MicroPython 中的垃圾收集功能可通过gc
内置模块获得:
>>> x = 5
>>> x
5
>>> import gc
>>> gc.enable()
>>> gc.mem_alloc()
1312
>>> gc.mem_free()
2071392
>>> gc.collect()
19
>>> gc.disable()
>>>
即使在gc.disable()
被调用时,也可以使用 触发收集gc.collect()
。
对象模型¶
所有 MicroPython 对象都由 mp_obj_t
数据类型引用。这通常是字大小(即与目标架构上的指针大小相同),通常可以是 32 位(STM32、nRF、ESP32、Unix x86)或 64 位(Unix x64)。对于某些对象表示,它也可以大于字大小,例如在 32 位体系结构上 OBJ_REPR_D
具有 64 位大小mp_obj_t
。
mp_obj_t
表示 MicroPython 对象,例如整数、浮点数、类型、字典或类实例。一些对象,如布尔值和小整数,它们的值直接存储在 mp_obj_t
值中,不需要额外的内存。其他对象将它们的值存储在内存中的其他地方(例如在垃圾收集堆上),并且它们mp_obj_t
包含指向该内存的指针。的一部分mp_obj_t
是表明它是什么类型的对象的标签。
有关 py/mpconfig.h
可用表示的具体细节,请参阅。
指针标记
因为指针是字对齐的,所以当它们存储在mp_obj_t
这个对象句柄的低位时将为零。例如,在 32 位架构上,低 2 位将为零:
********|********|********|******00
这些位保留用于存储标签。标签存储额外的信息,而不是引入一个新的字段来存储对象中的信息,这可能是低效的。在 MicroPython 中,标签告诉我们是在处理一个小整数、内部(小)字符串还是一个具体对象,并且不同的语义适用于这些对象中的每一个。
对于小整数,映射是这样的:
********|********|********|*******1
其中星号保存实际的整数值。对于内部字符串或直接对象(例如True
),mp_obj_t
值的布局分别为:
********|********|********|*****010
********|********|********|*****110
而不是上述任何一种的具体对象采用以下形式:
********|********|********|******00
这里的星星对应于内存中具体对象的地址。
对象的分配¶
小整数的值直接存储在 中mp_obj_t
,并将就地分配,而不是在堆或其他地方。因此,创建小整数不会影响堆。对于已经将其文本数据存储在其他地方的实习字符串,以及诸如None
, False
和 之类的直接值,类似 True
。
作为具体对象的其他所有内容都在堆上分配,其对象结构是这样的,即在对象头中保留一个字段来存储对象的类型。
+++++++++++
+ +
+ type + object header
+ +
+++++++++++
+ + object items
+ +
+ +
+++++++++++
堆的最小分配单位是块,大小为四个机器字(32 位机器上为 16 字节,64 位机器上为 32 字节)。在堆上分配的另一个结构跟踪每个块中对象的分配。这种结构称为位图。
位图跟踪块是“空闲”还是“使用中”,并使用两位来跟踪每个块的状态。
标记清除垃圾收集器管理在堆上分配的对象,并且还利用位图来标记仍在使用的对象。有关 这些细节的完整实现,请参见py/gc.c。
分配:堆布局
堆被安排成由池中的块组成。一个块可以有不同的属性:
ATB:(分配表字节)如果设置,则该块为普通块
FREE: :自由块
HEAD: 区块链的头部
TAIL:在区块链的尾部
MARK :标记的磁头块
FTB:(终结器表字节)如果设置,则该块具有终结器