diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt
index 7a36b430f1927e58becbe83fb8bb31adc0c3a71d..ff64cbe1e06b385e302e4cc6fbb944e7baf1809b 100644
--- a/RELEASE_NOTES.txt
+++ b/RELEASE_NOTES.txt
@@ -1,3 +1,9 @@
+---------------------------------------------------------------------
+2.1.0 (Oct 9, 2024) - Changes since version 2.0.2
+
+- Fix memory leak due to circular references to Output objects in list App.outputs
+- Breaking change : replaced App.outputs by a tuple of out image keys (App._out_image_keys)
+
 ---------------------------------------------------------------------
 2.0.2 (Apr 5, 2024) - Changes since version 2.0.1
 
@@ -5,13 +11,11 @@
 - Fix a bug with parameters of type "field" for vector files
 - Fix wrong output parameter key in ImageClassifier and ImageClassifierFromDeepFeatures
 
-
 ---------------------------------------------------------------------
 2.0.1 (Dec 18, 2023) - Changes since version 2.0.0
 
 - Fix a bug when writing outputs in uint8
 
-
 ---------------------------------------------------------------------
 2.0.0 (Nov 23, 2023) - Changes since version 1.5.4
 
diff --git a/pyotb/__init__.py b/pyotb/__init__.py
index 594272bfe336efe543b3a8bea44c6649373848fe..5e5831636cf19b8419a9d9c33afb655ca35fbab4 100644
--- a/pyotb/__init__.py
+++ b/pyotb/__init__.py
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 """This module provides convenient python wrapping of otbApplications."""
-__version__ = "2.0.3.dev2"
+__version__ = "2.1.0"
 
 from .install import install_otb
 from .helpers import logger
diff --git a/pyotb/core.py b/pyotb/core.py
index b78efd478d6c42533f78996ebfba0e70ff449fea..ee73fcbcaa91754afeb41d67b26c35aa49b2274e 100644
--- a/pyotb/core.py
+++ b/pyotb/core.py
@@ -579,7 +579,7 @@ class App(OTBObject):
         self._exports_dic = {}
         self._settings, self._auto_parameters = {}, {}
         self._time_start, self._time_end = 0.0, 0.0
-        self.data, self.outputs = {}, {}
+        self.data = {}
         self.quiet, self.frozen = quiet, frozen
 
         # Param keys and types
@@ -597,17 +597,15 @@ class App(OTBObject):
             for key in self.parameters_keys
             if self.app.GetParameterType(key) == otb.ParameterType_Choice
         }
+        self._out_image_keys = tuple(
+            key
+            for key, param in self._out_param_types.items()
+            if param == otb.ParameterType_OutputImage
+        )
 
         # Init, execute and write (auto flush only when output param was provided)
         if args or kwargs:
             self.set_parameters(*args, **kwargs)
-        # Create Output image objects
-        for key in (
-            key
-            for key, param in self._out_param_types.items()
-            if param == otb.ParameterType_OutputImage
-        ):
-            self.outputs[key] = Output(self, key, self._settings.get(key))
 
         if not self.frozen:
             self.execute()
@@ -643,8 +641,8 @@ class App(OTBObject):
         return self._all_param_types[key] in param_types
 
     def __is_multi_output(self):
-        """Check if app has multiple outputs to ensure re-execution during write()."""
-        return len(self.outputs) > 1
+        """Check if app has multiple image outputs to ensure re-execution in write()."""
+        return len(self._out_image_keys) > 1
 
     def is_input(self, key: str) -> bool:
         """Returns True if the parameter key is an input."""
@@ -745,10 +743,8 @@ class App(OTBObject):
                     f"{self.name}: error before execution,"
                     f" while setting '{key}' to '{obj}': {e})"
                 ) from e
-            # Save / update setting value and update the Output object initialized in __init__ without a filepath
+            # Save / update setting value
             self._settings[key] = obj
-            if key in self.outputs:
-                self.outputs[key].filepath = obj
             if key in self._auto_parameters:
                 del self._auto_parameters[key]
 
@@ -1104,8 +1100,8 @@ class App(OTBObject):
         if isinstance(key, str):
             if key in self.data:
                 return self.data[key]
-            if key in self.outputs:
-                return self.outputs[key]
+            if key in self._out_image_keys:
+                return Output(self, key, self._settings.get(key))
             if key in self.parameters:
                 return self.parameters[key]
             raise KeyError(f"{self.name}: unknown or undefined parameter '{key}'")
@@ -1538,7 +1534,7 @@ class Output(OTBObject):
         mkdir: bool = True,
     ):
         """Constructor for an Output object, initialized during App.__init__."""
-        self.parent_pyotb_app = pyotb_app  # keep trace of parent app
+        self.parent_pyotb_app = pyotb_app  # keep a reference to parent app
         self.param_key = param_key
         self.filepath = filepath
         if mkdir and filepath is not None: