You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何避免树莓派Pico上MicroPython垃圾回收延迟及内存泄漏?

问题与解决方案

问题描述

在树莓派Pico上使用MicroPython处理脉冲波(17ms高电平、3ms低电平)时,遇到两个核心问题:

  • 垃圾回收(GC)触发时会导致输出异常延迟约6ms,超出需求范围
  • 禁用GC(gc.disable())后,程序仅运行约1秒就因内存耗尽崩溃

以下测试代码仅更新类实例属性,却出现持续内存增长:

from gc import mem_alloc
from time import ticks_us, ticks_diff

class Example_class:
    def __init__(self,a):
        self.a = a
        self.b = 1

example_object = Example_class(1)
start_time = ticks_us()

while True:
    example_object.a=0.000001 * ticks_diff(ticks_us(),start_time)
    m0 = mem_alloc() # 用于演示内存变化
    example_object.b=example_object.a*1
    m1 = mem_alloc() # 用于演示内存变化
    print(m1-m0) # 输出每次循环新增内存
    print(mem_alloc()) # 输出当前已分配内存

测试发现:

  • print(m1-m0)始终返回16,说明每次循环都新增16字节内存
  • mem_alloc()结果持续增长,禁用GC后程序数秒内崩溃
  • 将乘数0.000001改为1后问题消失,random.randint(0,1)等操作也会引发类似内存增长

问题原因

  1. 浮点数的不可变性:MicroPython中浮点数是不可变对象,每次浮点数计算(如0.000001 * ticks_diff(...))都会在堆上创建新的浮点数对象,旧对象被丢弃后,若GC禁用则无法被回收,导致内存持续累积。
  2. 小整数对象池复用:当乘数改为1时,计算结果为整数,MicroPython对小整数(通常是-5到256)采用对象池复用机制,不会创建新对象,因此无内存增长。
  3. 额外操作的对象创建:random.randint或print等操作会创建临时字符串/对象,同样会占用堆内存,在GC禁用时无法回收。

解决方案

1. 完全用整数运算替代浮点数

浮点数是内存增长的核心原因,将所有计算转换为整数操作:

  • 保留微秒级整数计时(ticks_us返回的整数),避免浮点缩放。例如将0.000001 * ticks_diff(...)改为ticks_diff(...) // 1000000(得到秒级整数),或直接用微秒整数进行后续逻辑计算。
  • 示例修改:
from gc import mem_alloc
from time import ticks_us, ticks_diff

class Example_class:
    def __init__(self,a):
        self.a = a
        self.b = 1

example_object = Example_class(1)
start_time = ticks_us()

while True:
    # 用整数除法替代浮点乘法,得到秒级整数
    example_object.a = ticks_diff(ticks_us(), start_time) // 1000000
    m0 = mem_alloc()
    example_object.b = example_object.a * 1
    m1 = mem_alloc()
    print(m1 - m0)  # 此时输出应为0,无新增内存

2. 手动控制GC触发时机

不要完全禁用GC,而是在脉冲波的低电平时间段(3ms)手动调用gc.collect(),这段时间对输出延迟不敏感:

import gc
from machine import Pin
from time import ticks_us, ticks_diff, sleep_ms

gc.disable() # 先禁用自动GC
pulse_pin = Pin(15, Pin.OUT)

while True:
    # 输出17ms高电平(核心逻辑,无GC)
    pulse_pin.value(1)
    # 执行高电平期间的逻辑,确保无内存泄漏
    # ...
    
    # 进入3ms低电平,手动触发GC
    pulse_pin.value(0)
    gc.collect()
    sleep_ms(3) # 补足低电平时间

3. 移除不必要的调试操作

print语句会创建大量临时字符串对象,调试完成后必须移除,避免额外内存消耗。

4. 预分配必要对象(如必须用浮点数)

若业务逻辑必须使用浮点数,可预先创建浮点数变量并复用,但MicroPython浮点数不可变,因此需用其他方式优化:

  • 例如将浮点数计算结果存储到预先分配的列表或对象的属性中,避免每次创建新对象(实际效果有限,优先推荐整数方案)。

内容的提问来源于stack exchange,提问作者Jack

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 14:42:14