Page 1 of 1

[HOW-TO] Unload modules from RAM

Posted: Tue Sep 25, 2018 9:05 am
by kevinkk525
I'd like to give a short documentation about how to unload a module from RAM, especially one that is part of a package as that gave me a lot of headache and had some difficulties that were not obvious at first. You can read the discussion, examples and tests in this thread: viewtopic.php?f=2&t=5288
So I'd like to share the knowledge we gathered for anyone who's interested, struggling with the same problem or just to prevent the same questions from being asked again.
All tests were done on an esp8266 with micropython 1.9.4.

1) Project structure:
Let's assume you have a project with a structure like this:

Code: Select all

- rootpackage
--- package
----- subpackage
------- module
Every package has an empty "__init__.py" and your module does not have any import statements (just to keep it simply and reproducible).


2) Importing and unloading the module:

2.1) Now you can import your module and normally you would do it like this:

Code: Select all

from rootpackage.package.subpackage import module
Unloading the module you would expect this to work:

Code: Select all

del module
del sys.modules["rootpackage.package.subpackage.module"]
This however does not work, you have to do one of these solutions:
2.1. A) Delete all package references to the module in sys.modules

Code: Select all

del sys.modules["rootpackage.package.subpackage"]
del sys.modules["rootpackage.package"]
del sys.modules["rootpackage"]
Only now the RAM gets freed. The reason for this behaviour seems to be that the root package holds the reference to the module.
This solution however should not be used at all, as removing packages from sys.modules that might be used somewhere else, can result in problems.

2.1. B) Delete the module reference in the root package:
For this you have to import the root package "rootpackage" so that it is visible in "dir()".

Code: Select all

import rootpackage
del rootpackage.package.subpackage.module
This works fine but is not the most intuitive way as you normally don't import the root package.


2.2) Alternative method of importing a module:

Code: Select all

import rootpackage.package.subpackage.module
Of course that also means that you have to use this complete expression everywhere you refer to your module (e.g. rootpackage.package.subpackage.module.do_stuff())
Unloading this module however is a lot easier and works as expected:

Code: Select all

del rootpackage.package.subpackage.module
del sys.modules["rootpackage.package.subpackage.module"]
The RAM of the module will get freed but the packages still remain in sys.modules. And that's how it should be, because in a project, you'll likely have many modules within the same root packages.

3) Testing it:
If you want to test this, make sure to run this command between every other command:

Code: Select all

gc.collect();gc.mem_free();dir();sys.modules
Only then you get a good overview of what's going on. If you read the linked thread, you'll read a lot about the tests we did.

4) Warnings:
- Depending on your module it might not be possible to release any RAM. A simple function designed to consume some RAM like this resulted in no released RAM in most of the tests, so check your modules first (this is however not a function you'd use in a module):

Code: Select all

def b():
b();b();b();b();b();b();b();b();b();b();b();b();b();b();b();b();b();b();b();b();b();b();b();b();b();b();b();b();b();b();b();b();b();b();b();b();b();b();b(); return 0
- If you hold a reference to the module you want to unload, it won't work either.
- Always prefer method 2.2. of this How-To as this is the most reliable and "non-hacky" way to import and unload a module.