Code: Select all
a = object_a_that_allocates()
a = object_b_that_allocates()
As an aside I have found that periodically performing explicit calls to gc.collect() reduces fragmentation.
Code: Select all
a = object_a_that_allocates()
a = object_b_that_allocates()
It doesn't do this by itself. On an esp8266 with initially 32k of free RAM the following test fails as expected on the second assignment to "b":pythoncoder wrote: ↑Sat Dec 21, 2019 6:39 am@jimmo I find that last example surprising, please could you explain? If I writeby line 2 the instance of object_a_that_allocates has no references to it. Why won't the GC free its RAM? How does assigning None change this?Code: Select all
a = object_a_that_allocates() a = object_b_that_allocates()
Code: Select all
import gc
gc.collect()
print(gc.mem_free())
a = []
for i in range(1, 31):
a.append('x' * 1000)
print(gc.mem_free())
b = 'x' * 1000
b = 'y' * 1000
print(gc.mem_free())
Code: Select all
for string in list_object:
a=do_something(string)
send_string(a)
a=None
gc.collect()
Code: Select all
a = ...something that allocates...
gc.collect()
a = ...something else that allocates...
Code: Select all
a = ...something that allocates...
a = None
gc.collect()
a = ...something else that allocates...
Code: Select all
MicroPython v1.12 on 2019-12-20; ESP module with ESP8266
Type "help()" for more information.
>>> import gc
>>> gc.threshold()
9488
In this case i would do this:kevinkk525 wrote: ↑Sun Dec 22, 2019 7:26 amCode: Select all
for string in list_object: a=do_something(string) send_string(a) a=None gc.collect()
Code: Select all
for string in list_object:
send_string(do_something(string))
gc.collect()
Code: Select all
def foo():
for string in list_object:
a = do_something(string)
send_string(a)
foo()
gc.collect() # 'a' should be cleared, isn't it?
Code: Select all
a = foo()
a = bar()
Code: Select all
a = None
Yep, exactly what dhylands said. Until `a` is assigned the second time (which happens _after_ the new allocation), the reference to the old data is still live, so cannot be freed.pythoncoder wrote: ↑Sun Dec 22, 2019 6:54 amWhat baffles me is the assertion from @jimmo that assigning None has any effect on behaviour: if this is true I'd like to know the reason.
When the allocator fails to allocate, it always runs a gc.collect first then tries again before actually failing the allocation.MostlyHarmless wrote: ↑Sat Dec 21, 2019 2:43 pmAssigning None and calling gc.collect() does. This means that an out of memory doesn't try running gc.collect() by itself.
Code: Select all
>>> import gc, micropython
>>> micropython.mem_info()
mem: total=5528, current=1582, peak=3161
stack: 960 out of 80000
GC: total: 16128, used: 2752, free: 13376
No. of 1-blocks: 54, 2-blocks: 11, max blk sz: 6, max free sz: 413
>>> a = bytearray(10*1024)
>>> a = bytearray(10*1024)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
MemoryError: memory allocation failed, allocating 10240 bytes
>>> a = None
>>> a = bytearray(10*1024) # This works
Yes, this is exactly the scenario where assigning to None will help.kevinkk525 wrote: ↑Sun Dec 22, 2019 7:26 amThe only situation where I assign None is in a loop:
...
Here it makes sense so the temporary veriable a gets collected after each iteration.
Yes, this is another way of achieving the same thing. When a function returns it's local variables are now out of scope and will not be found by the gc.
Code: Select all
some_program_state = something()
def update():
global some_program_state
try:
some_program_state = maybe_get_new_program_state()
except:
pass
Yes -- frequent gc.collect() will help with fragmentation. I'm not sure I have a good way to put this into words without a diagram but essentially if you can synchronise your collections with your program "loop", then you'll end up playing out the same allocation pattern across RAM every time.pythoncoder wrote: ↑Sat Dec 21, 2019 6:39 amAs an aside I have found that periodically performing explicit calls to gc.collect() reduces fragmentation.
This is related to the above. It changes the code that I described above where it will do a gc.collect() if it fails to find enough free heap, into preemptively collecting if the available heap drops below a threshold.