The code is crystal clear but I do not understand
1. what is this heap for,
2. what data is stored in this heap, and
3. where would this data be stored otherwise (i.e. without compiling gc).
4. Does the presence of heap/gc affect, for example, the location of string contents created by mp_obj_new_str?

The code is crystal clear but I do not understand
1. what is this heap for,
2. what data is stored in this heap, and
3. where would this data be stored otherwise (i.e. without compiling gc).
4. Does the presence of heap/gc affect, for example, the location of string contents created by mp_obj_new_str?

1. Heap is a contiguous block of memory that uPy uses for dynamically created objects.
2. Anything created at runtime
3. gc is separate from heap. You don’t need gc but you do need heap.
4. You need a heap to create new strings.

Regarding p.3: I do not have any explicit heap initialization but it still works somehow. ESP8266 port runs gc_init(heap, heap+sizeof(heap)). What is this invocation? What is this heap? Obviously, it is not necessarily the actual heap: the 36Kb chunk 'heap' may appear inside a global stack. Where do I define heap limits in micropython?

You can check in your program how the memory is getting allocated using micropython.mem_info() from micropython module. Use gc.collect() from gc module to free blocks that are not been used anymore on the heap, you can see it working on code. Right now you will get something like this from mem_info():

So definitely there is something wrong. The most important value here is the total value on the GC: line, that's the heap memory size that is used for about every command we give in uPy.

From what I got to understand of this port, is that it uses the C++ CSDK to create the main thread that is always necessary (as pointed here https://ai-thinker-open.github.io/GPRS_ ... -code.html) and than it creates a second thread that is the micropython thread. This threads is called MycroPyTask and it defines the stack and heap size, and also starts the gc. This all happens on main.c, basically on this part:

So checking there, the stack size is set for 2048 * 4 * 4 -1024, exactly the 31744 that appears at the stack size when we run mem_info().
Also, heap size is set as h_size = 4 * 1024, that is 3968 from the heap total +128bits that mem_info uses. I decided to play with these values, when changing stack size value I couldn't connect anymore to the module, tried some different values and went back to the one it is. Changing heap size values was a success, I've tried many different ones and the best I got was 59968, so almost 60k from what was 4k. I used h_size = 2048 * 30 for this. I've tested the available heap memory by allocating buffers and could use all this memory. Right now mem_info() gives me:

Well, the question is how much contiguous memory can be allocated. I will stick to 512k (well, a binary search can be applied here but I do not think it is import right now). The good news is that mp finaliser is working! Deleting a file pointer and calling gc.colllect will actually close the file!

Yet, the port was working without gc at all and it seems mysterious. MP could even throw OOM at some point.

Another gc-related question I have right now is: whether is is important to look into registers for root pointers in a single-threaded mp?