Garbage Collection Not Working

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
User avatar
jimmo
Posts: 1633
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: Garbage Collection Not Working

Post by jimmo » Wed Jan 22, 2020 12:19 am

It's important for the port to provide a gc_collect() because it's responsible for figuring out how to extract root pointers from the stack and CPU registers (in a port-specific way).

When you say "I am trying to use MICROPY_ENABLE_GC" I assume this means you have actually enabled the GC.

To explain your earlier issue, what you're doing is essentially the same as

Code: Select all

a = [1]*1000
a = [2]*1000
It can't free the old data for "a" until after it has assigned the new value for a, so the peak memory is always 2000 items.

The solution is to either "soft reset" MicroPython in between scripts, or to change your code such that nothing is left in global scope (e.g. with a function as described). Or manually take things out of scope using "del a".

okayserh
Posts: 1
Joined: Wed May 20, 2020 8:23 pm

Re: Garbage Collection Not Working

Post by okayserh » Wed May 20, 2020 8:48 pm

Hi,

since my problem (if it actually is one) pretty much fits into the subject, I have continued this thread. I have an STM32F746-Discovery board and have "ported" MicroPython to work with it. One "non-standard" thing is that I start it from "u-boot", which resides in the internal flash, while I intended to have MicroPython running completely in the (external) RAM. Thus, I've modified a few lines of code regarding the HW initialization, since "u-boot" already initializes most of the hardware. Furthermore, I've added a quick hack to use the included LCD as framebuffer. When I tried an adjusted version of the Mandelbrot program, it stopped halfway. After analysing the crash, I figured out that:
1) The GC runs full
2) Automated GC is not triggered when running out of space
3) Adding either an explicit GC Threshold (gc.threshold(50000)) or calling the Garbage collection (gc.collect ()) once in a while fixes the problem

The minimal program is:

Code: Select all

#
# Quick sample to try a mandelbrot

import pyb
import gc

def test_mandel():
    # function to compute Mandelbrot pixels
    def in_set(c):
        z = 0
        for i in range(40):
            z = z * z + c
            if abs(z) > 100:
                return i
        return 0

    # cache width and height of LCD
    w = 200
    h = 200

    # draw the Mandelbrot set line-by-line
    hh = (h - 1) / 3.2
    ww = (w - 1) / 2.4
    for v in range(h):
        pyb.info ()
        for u in range(w):
            c = in_set((v / hh - 2.3) + (u / ww - 1.2) * 1j)
            if c < 16:
                rgb = c << 12 | c << 6
            else:
                rgb = 0xF800 | c << 6

def test_all ():
    gc.enable ()
    test_mandel ()
When I let this run I observe the following behaviour (just the output of pyb.info () between two iterations)

Code: Select all

ID=42002100:0f513830:37393131
S=200000000
H=200000000
P1=50000000
P2=100000000
_etext=c006b670
_sidata=c006b678
_sdata=c006b678
_edata=c006b6d4
_sbss=c0080000
_ebss=c0106f34
_sstack=c07f7ff8
_estack=c07ffff8
_ram_start=c0000400
_heap_start=c0106f34
_heap_end=c07f7ff8
_ram_end=c0800000
qstr:
  n_pool=1
  n_qstr=7
  n_str_data_bytes=50
  n_total_bytes=146
GC:
  6147328 total
  966832 : 5180496
  1=60331 2=7 m=40
ID=42002100:0f513830:37393131
S=200000000
H=200000000
P1=50000000
P2=100000000
_etext=c006b670
_sidata=c006b678
_sdata=c006b678
_edata=c006b6d4
_sbss=c0080000
_ebss=c0106f34
_sstack=c07f7ff8
_estack=c07ffff8
_ram_start=c0000400
_heap_start=c0106f34
_heap_end=c07f7ff8
_ram_end=c0800000
qstr:
  n_pool=1
  n_qstr=7
  n_str_data_bytes=50
  n_total_bytes=146
GC:
  6147328 total
  1029872 : 5117456
  1=64271 2=7 m=40
So, the GC continuously grows, until I either clean it up manually, or it crashes. I tried to replicate the behaviour with the Unix-port of MicroPython, but presumably it uses a different garbage collection!? At least I was not able to replicate the obsered behaviour.

Since I'm a bit lost with this issue, is that the expected behaviour of the GC or shouldn't it automatically discard unused stuff when the function "in_set" returns?

Many thanks for your help!

Best Regards,
Oliver

Post Reply